github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/pkg/binlog/event/event.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  	"bytes"
    21  	"encoding/binary"
    22  
    23  	gmysql "github.com/go-mysql-org/go-mysql/mysql"
    24  	"github.com/go-mysql-org/go-mysql/replication"
    25  	"github.com/pingcap/tiflow/dm/pkg/gtid"
    26  	"github.com/pingcap/tiflow/dm/pkg/terror"
    27  )
    28  
    29  // flags used in RowsEvent.
    30  const (
    31  	RowFlagsEndOfStatement     uint16 = 0x0001
    32  	RowFlagsNoForeignKeyChecks uint16 = 0x0002
    33  	RowFlagsNoUniqueKeyChecks  uint16 = 0x0004
    34  	RowFlagsRowHasAColumns     uint16 = 0x0008
    35  )
    36  
    37  const (
    38  	// GTIDFlagsCommitYes represents a GTID flag with [commit=yes].
    39  	// in `Row` binlog format, this will appear in GTID event before DDL query event.
    40  	GTIDFlagsCommitYes uint8 = 1
    41  
    42  	binlogVersion   uint16 = 4 // only binlog-version 4 supported now
    43  	mysqlVersion           = "5.7.22-log"
    44  	mysqlVersionLen        = 50                                 // fix-length
    45  	eventHeaderLen         = uint8(replication.EventHeaderSize) // always 19
    46  	crc32Len        uint32 = 4                                  // CRC32-length
    47  	tableMapFlags   uint16 = 1                                  // flags in TableMapEvent's post-header, not used yet
    48  
    49  	// MinUserVarEventLen represents the minimum event length for a USER_VAR_EVENT with checksum.
    50  	MinUserVarEventLen = uint32(eventHeaderLen+4+1+1) + crc32Len // 29 bytes
    51  	// MinQueryEventLen represents the minimum event length for a QueryEvent with checksum.
    52  	MinQueryEventLen = uint32(eventHeaderLen+4+4+1+2+2+1+1) + crc32Len // 38 bytes
    53  )
    54  
    55  var (
    56  	// A array indexed by `Binlog-Event-Type - 1` to extract the length of the event specific header.
    57  	// It is copied from a binlog file generated by MySQL 5.7.22-log.
    58  	// The doc at https://dev.mysql.com/doc/internals/en/format-description-event.html does not include all of them.
    59  	eventTypeHeaderLen = []byte{
    60  		0x38, 0x0d, 0x00, 0x08, 0x00, 0x12, 0x00, 0x04, 0x04, 0x04, 0x04, 0x12, 0x00, 0x00, 0x5f, 0x00,
    61  		0x04, 0x1a, 0x08, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x02, 0x00, 0x00, 0x00, 0x0a, 0x0a, 0x0a,
    62  		0x2a, 0x2a, 0x00, 0x12, 0x34, 0x00,
    63  	}
    64  	// user var name used in dummy USER_VAR_EVENT.
    65  	dummyUserVarName = []byte("!dummyvar")
    66  	// dummy (commented) query in a QueryEvent.
    67  	dummyQuery = []byte("# dummy query generated by DM, often used to fill a hole in a binlog file")
    68  )
    69  
    70  // GenEventHeader generates a EventHeader's raw data according to a passed-in EventHeader struct.
    71  // ref: https://dev.mysql.com/doc/internals/en/binlog-event-header.html
    72  func GenEventHeader(header *replication.EventHeader) ([]byte, error) {
    73  	buf := new(bytes.Buffer)
    74  
    75  	// timestamp, 4 bytes
    76  	err := binary.Write(buf, binary.LittleEndian, header.Timestamp)
    77  	if err != nil {
    78  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write timestamp %d", header.Timestamp)
    79  	}
    80  
    81  	// event_type, 1 byte
    82  	err = binary.Write(buf, binary.LittleEndian, header.EventType)
    83  	if err != nil {
    84  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write event_type %v", header.EventType)
    85  	}
    86  
    87  	// server_id, 4 bytes
    88  	err = binary.Write(buf, binary.LittleEndian, header.ServerID)
    89  	if err != nil {
    90  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write server_id %d", header.ServerID)
    91  	}
    92  
    93  	// event_size, 4 bytes
    94  	err = binary.Write(buf, binary.LittleEndian, header.EventSize)
    95  	if err != nil {
    96  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write event_size %d", header.EventSize)
    97  	}
    98  
    99  	// log_pos, 4 bytes
   100  	err = binary.Write(buf, binary.LittleEndian, header.LogPos)
   101  	if err != nil {
   102  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write log_pos %d", header.LogPos)
   103  	}
   104  
   105  	// flags, 2 bytes
   106  	err = binary.Write(buf, binary.LittleEndian, header.Flags)
   107  	if err != nil {
   108  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write flags % X", header.Flags)
   109  	}
   110  
   111  	// try to decode the data
   112  	eh := replication.EventHeader{}
   113  	err = eh.Decode(buf.Bytes())
   114  	if err != nil {
   115  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "decode % X", buf.Bytes())
   116  	}
   117  
   118  	return buf.Bytes(), nil
   119  }
   120  
   121  // GenFormatDescriptionEvent generates a FormatDescriptionEvent.
   122  // ref: https://dev.mysql.com/doc/internals/en/format-description-event.html.
   123  func GenFormatDescriptionEvent(header *replication.EventHeader, latestPos uint32) (*replication.BinlogEvent, error) {
   124  	payload := new(bytes.Buffer)
   125  
   126  	// binlog-version, 2 bytes
   127  	err := binary.Write(payload, binary.LittleEndian, binlogVersion)
   128  	if err != nil {
   129  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write binlog-version %d", binlogVersion)
   130  	}
   131  
   132  	// mysql-server version, 50 bytes
   133  	serverVer := make([]byte, mysqlVersionLen)
   134  	copy(serverVer, mysqlVersion)
   135  	err = binary.Write(payload, binary.LittleEndian, serverVer)
   136  	if err != nil {
   137  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write mysql-server version %v", serverVer)
   138  	}
   139  
   140  	// create_timestamp, 4 bytes
   141  	err = binary.Write(payload, binary.LittleEndian, header.Timestamp)
   142  	if err != nil {
   143  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write create_timestamp %d", header.Timestamp)
   144  	}
   145  
   146  	// event_header_length, 1 byte
   147  	err = binary.Write(payload, binary.LittleEndian, eventHeaderLen)
   148  	if err != nil {
   149  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write event_header_length %d", eventHeaderLen)
   150  	}
   151  
   152  	// event type header length, 38 bytes now
   153  	err = binary.Write(payload, binary.LittleEndian, eventTypeHeaderLen)
   154  	if err != nil {
   155  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write event type header length % X", eventTypeHeaderLen)
   156  	}
   157  
   158  	// checksum algorithm, 1 byte
   159  	err = binary.Write(payload, binary.LittleEndian, replication.BINLOG_CHECKSUM_ALG_CRC32)
   160  	if err != nil {
   161  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write checksum algorithm % X", replication.BINLOG_CHECKSUM_ALG_CRC32)
   162  	}
   163  
   164  	buf := new(bytes.Buffer)
   165  	event := &replication.FormatDescriptionEvent{}
   166  	ev, err := assembleEvent(buf, event, true, *header, replication.FORMAT_DESCRIPTION_EVENT, latestPos, nil, payload.Bytes())
   167  	return ev, err
   168  }
   169  
   170  // GenRotateEvent generates a RotateEvent.
   171  // ref: https://dev.mysql.com/doc/internals/en/rotate-event.html
   172  func GenRotateEvent(header *replication.EventHeader, latestPos uint32, nextLogName []byte, position uint64) (*replication.BinlogEvent, error) {
   173  	if len(nextLogName) == 0 {
   174  		return nil, terror.ErrBinlogEmptyNextBinName.Generate()
   175  	}
   176  
   177  	// Post-header
   178  	postHeader := new(bytes.Buffer)
   179  	err := binary.Write(postHeader, binary.LittleEndian, position)
   180  	if err != nil {
   181  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write position %d", position)
   182  	}
   183  
   184  	// Payload
   185  	payload := new(bytes.Buffer)
   186  	err = binary.Write(payload, binary.LittleEndian, nextLogName)
   187  	if err != nil {
   188  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write next binlog name % X", nextLogName)
   189  	}
   190  
   191  	buf := new(bytes.Buffer)
   192  	event := &replication.RotateEvent{}
   193  	ev, err := assembleEvent(buf, event, false, *header, replication.ROTATE_EVENT, latestPos, postHeader.Bytes(), payload.Bytes())
   194  	return ev, err
   195  }
   196  
   197  // GenPreviousGTIDsEvent generates a PreviousGTIDsEvent.
   198  // MySQL has no internal doc for PREVIOUS_GTIDS_EVENT.
   199  // we ref:
   200  //
   201  //	a. https://github.com/vitessio/vitess/blob/28e7e5503a6c3d3b18d4925d95f23ebcb6f25c8e/go/mysql/binlog_event_mysql56.go#L56
   202  //	b. https://dev.mysql.com/doc/internals/en/com-binlog-dump-gtid.html
   203  func GenPreviousGTIDsEvent(header *replication.EventHeader, latestPos uint32, gSet gmysql.GTIDSet) (*replication.BinlogEvent, error) {
   204  	if gSet == nil {
   205  		return nil, terror.ErrBinlogEmptyGTID.Generate()
   206  	}
   207  
   208  	// event payload, GTID set encoded in it
   209  	payload := gSet.Encode()
   210  
   211  	buf := new(bytes.Buffer)
   212  	event := &replication.PreviousGTIDsEvent{}
   213  	ev, err := assembleEvent(buf, event, false, *header, replication.PREVIOUS_GTIDS_EVENT, latestPos, nil, payload)
   214  	return ev, err
   215  }
   216  
   217  // GenGTIDEvent generates a GTIDEvent.
   218  // MySQL has no internal doc for GTID_EVENT.
   219  // we ref the `GTIDEvent.Decode` in go-mysql.
   220  // `uuid` is the UUID part of the GTID, like `9f61c5f9-1eef-11e9-b6cf-0242ac140003`.
   221  // `gno` is the GNO part of the GTID, like `6`.
   222  func GenGTIDEvent(header *replication.EventHeader, latestPos uint32, gtidFlags uint8, uuid string, gno int64, lastCommitted int64, sequenceNumber int64) (*replication.BinlogEvent, error) {
   223  	return genGTIDEventInner(header, latestPos, gtidFlags, uuid, gno, lastCommitted, sequenceNumber, replication.GTID_EVENT)
   224  }
   225  
   226  func GenAnonymousGTIDEvent(header *replication.EventHeader, latestPos uint32, gtidFlags uint8, lastCommitted int64, sequenceNumber int64) (*replication.BinlogEvent, error) {
   227  	return genGTIDEventInner(header, latestPos, gtidFlags, "00000000-0000-0000-0000-000000000000", 0, lastCommitted, sequenceNumber, replication.ANONYMOUS_GTID_EVENT)
   228  }
   229  
   230  func genGTIDEventInner(header *replication.EventHeader, latestPos uint32, gtidFlags uint8, uuid string, gno int64, lastCommitted int64, sequenceNumber int64, eventType replication.EventType) (*replication.BinlogEvent, error) {
   231  	payload := new(bytes.Buffer)
   232  
   233  	// GTID flags, 1 byte
   234  	err := binary.Write(payload, binary.LittleEndian, gtidFlags)
   235  	if err != nil {
   236  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write GTID flags % X", gtidFlags)
   237  	}
   238  
   239  	// SID, 16 bytes
   240  	sid, err := ParseSID(uuid)
   241  	if err != nil {
   242  		return nil, terror.Annotatef(err, "parse UUID %s to SID", uuid)
   243  	}
   244  	err = binary.Write(payload, binary.LittleEndian, sid.Bytes())
   245  	if err != nil {
   246  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write SID % X", sid.Bytes())
   247  	}
   248  
   249  	// GNO, 8 bytes
   250  	err = binary.Write(payload, binary.LittleEndian, gno)
   251  	if err != nil {
   252  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write GNO %d", gno)
   253  	}
   254  
   255  	// length of TypeCode, 1 byte
   256  	err = binary.Write(payload, binary.LittleEndian, uint8(replication.LogicalTimestampTypeCode))
   257  	if err != nil {
   258  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write length of TypeCode %d", replication.LogicalTimestampTypeCode)
   259  	}
   260  
   261  	// lastCommitted, 8 bytes
   262  	err = binary.Write(payload, binary.LittleEndian, lastCommitted)
   263  	if err != nil {
   264  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write last committed sequence number %d", lastCommitted)
   265  	}
   266  
   267  	// sequenceNumber, 8 bytes
   268  	err = binary.Write(payload, binary.LittleEndian, sequenceNumber)
   269  	if err != nil {
   270  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write sequence number %d", sequenceNumber)
   271  	}
   272  
   273  	buf := new(bytes.Buffer)
   274  	event := &replication.GTIDEvent{}
   275  	ev, err := assembleEvent(buf, event, false, *header, eventType, latestPos, nil, payload.Bytes())
   276  	return ev, err
   277  }
   278  
   279  // GenQueryEvent generates a QueryEvent.
   280  // ref: https://dev.mysql.com/doc/internals/en/query-event.html
   281  // ref: http://blog.51cto.com/yanzongshuai/2087782
   282  // `statusVars` should be generated out of this function, we can implement it later.
   283  // `len(query)` must > 0.
   284  func GenQueryEvent(header *replication.EventHeader, latestPos uint32, slaveProxyID uint32, executionTime uint32, errorCode uint16, statusVars []byte, schema []byte, query []byte) (*replication.BinlogEvent, error) {
   285  	if len(query) == 0 {
   286  		return nil, terror.ErrBinlogEmptyQuery.Generate()
   287  	}
   288  
   289  	// Post-header
   290  	postHeader := new(bytes.Buffer)
   291  
   292  	// slave_proxy_id (thread_id), 4 bytes
   293  	err := binary.Write(postHeader, binary.LittleEndian, slaveProxyID)
   294  	if err != nil {
   295  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write slave_proxy_id %d", slaveProxyID)
   296  	}
   297  
   298  	// executionTime, 4 bytes
   299  	err = binary.Write(postHeader, binary.LittleEndian, executionTime)
   300  	if err != nil {
   301  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write execution %d", executionTime)
   302  	}
   303  
   304  	// schema length, 1 byte
   305  	schemaLength := uint8(len(schema))
   306  	err = binary.Write(postHeader, binary.LittleEndian, schemaLength)
   307  	if err != nil {
   308  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write schema length %d", schemaLength)
   309  	}
   310  
   311  	// error code, 2 bytes
   312  	err = binary.Write(postHeader, binary.LittleEndian, errorCode)
   313  	if err != nil {
   314  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write error code %d", errorCode)
   315  	}
   316  
   317  	// status-vars length, 2 bytes
   318  	statusVarsLength := uint16(len(statusVars))
   319  	err = binary.Write(postHeader, binary.LittleEndian, statusVarsLength)
   320  	if err != nil {
   321  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write status-vars length %d", statusVarsLength)
   322  	}
   323  
   324  	// Payload
   325  	payload := new(bytes.Buffer)
   326  
   327  	// status-vars, status-vars length bytes
   328  	if statusVarsLength > 0 {
   329  		err = binary.Write(payload, binary.LittleEndian, statusVars)
   330  		if err != nil {
   331  			return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write status-vars % X", statusVars)
   332  		}
   333  	}
   334  
   335  	// schema, schema length bytes
   336  	if schemaLength > 0 {
   337  		err = binary.Write(payload, binary.LittleEndian, schema)
   338  		if err != nil {
   339  			return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write schema % X", schema)
   340  		}
   341  	}
   342  
   343  	// 0x00, 1 byte
   344  	err = binary.Write(payload, binary.LittleEndian, uint8(0x00))
   345  	if err != nil {
   346  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write 0x00")
   347  	}
   348  
   349  	// query, len(query) bytes
   350  	err = binary.Write(payload, binary.LittleEndian, query)
   351  	if err != nil {
   352  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write query % X", query)
   353  	}
   354  
   355  	buf := new(bytes.Buffer)
   356  	event := &replication.QueryEvent{}
   357  	ev, err := assembleEvent(buf, event, false, *header, replication.QUERY_EVENT, latestPos, postHeader.Bytes(), payload.Bytes())
   358  	return ev, err
   359  }
   360  
   361  // GenTableMapEvent generates a TableMapEvent.
   362  // ref: https://dev.mysql.com/doc/internals/en/table-map-event.html
   363  // ref: https://dev.mysql.com/doc/internals/en/describing-packets.html#type-lenenc_int
   364  // ref: http://blog.51cto.com/yanzongshuai/2090758
   365  // `len(schema)` must > 0, `len(table)` must > 0, `len(columnType)` must > 0.
   366  // `columnType` should be generated out of this function, we can implement it later.
   367  func GenTableMapEvent(header *replication.EventHeader, latestPos uint32, tableID uint64, schema []byte, table []byte, columnType []byte) (*replication.BinlogEvent, error) {
   368  	if len(schema) == 0 || len(table) == 0 || len(columnType) == 0 {
   369  		return nil, terror.ErrBinlogTableMapEvNotValid.Generate(schema, table, columnType)
   370  	}
   371  
   372  	// Post-header
   373  	postHeader := new(bytes.Buffer)
   374  
   375  	tableIDSize := 6 // for binlog V4, this should be 6.
   376  	if eventTypeHeaderLen[replication.TABLE_MAP_EVENT-1] == 6 {
   377  		tableIDSize = 4
   378  	}
   379  
   380  	// table id, tableIDSize bytes
   381  	err := binary.Write(postHeader, binary.LittleEndian, tableID)
   382  	if err != nil {
   383  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write table id %d", tableID)
   384  	}
   385  	postHeader.Truncate(tableIDSize) // truncate the unwanted bytes
   386  
   387  	// flags, 2 bytes
   388  	err = binary.Write(postHeader, binary.LittleEndian, tableMapFlags)
   389  	if err != nil {
   390  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write flags %d", tableMapFlags)
   391  	}
   392  
   393  	// Payload
   394  	payload := new(bytes.Buffer)
   395  
   396  	// schema name length, 1 byte
   397  	schemaLen := uint8(len(schema))
   398  	err = binary.Write(payload, binary.LittleEndian, schemaLen)
   399  	if err != nil {
   400  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write schema name length %d", schemaLen)
   401  	}
   402  
   403  	// schema name, schema name length bytes
   404  	err = binary.Write(payload, binary.LittleEndian, schema)
   405  	if err != nil {
   406  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write schema name % X", schema)
   407  	}
   408  
   409  	// 0x00, 1 byte
   410  	err = binary.Write(payload, binary.LittleEndian, uint8(0x00))
   411  	if err != nil {
   412  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write 0x00")
   413  	}
   414  
   415  	// table name length, 1 byte
   416  	tableLen := uint8(len(table))
   417  	err = binary.Write(payload, binary.LittleEndian, tableLen)
   418  	if err != nil {
   419  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write table name length %d", tableLen)
   420  	}
   421  
   422  	// table name, table name length bytes
   423  	err = binary.Write(payload, binary.LittleEndian, table)
   424  	if err != nil {
   425  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write table name % X", table)
   426  	}
   427  
   428  	// 0x00, 1 byte
   429  	err = binary.Write(payload, binary.LittleEndian, uint8(0x00))
   430  	if err != nil {
   431  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write 0x00")
   432  	}
   433  
   434  	// column-count, lenenc-int
   435  	columnCount := gmysql.PutLengthEncodedInt(uint64(len(columnType)))
   436  	err = binary.Write(payload, binary.LittleEndian, columnCount)
   437  	if err != nil {
   438  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write column-count % X", columnCount)
   439  	}
   440  
   441  	// column-type-def, column-count bytes
   442  	err = binary.Write(payload, binary.LittleEndian, columnType)
   443  	if err != nil {
   444  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write column-type-def % X", columnType)
   445  	}
   446  
   447  	// column-meta-def, lenenc-str
   448  	columnMeta, err := encodeTableMapColumnMeta(columnType)
   449  	if err != nil {
   450  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "generate column-meta-def for column-type-def % X", columnType)
   451  	}
   452  	err = binary.Write(payload, binary.LittleEndian, columnMeta)
   453  	if err != nil {
   454  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write column-meta-def % X", columnMeta)
   455  	}
   456  
   457  	// NULL-bitmask, (column-count + 8) / 7 bytes
   458  	bitMaskLen := bitmapByteSize(len(columnType))
   459  	bitMask := nullBytes(bitMaskLen)
   460  	err = binary.Write(payload, binary.LittleEndian, bitMask)
   461  	if err != nil {
   462  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write NULL-bitmask % X", bitMask)
   463  	}
   464  
   465  	buf := new(bytes.Buffer)
   466  	_, err = assembleEvent(buf, nil, false, *header, replication.TABLE_MAP_EVENT, latestPos, postHeader.Bytes(), payload.Bytes())
   467  	if err != nil {
   468  		return nil, terror.Annotate(err, "combine event data")
   469  	}
   470  
   471  	// sad, in order to Decode a TableMapEvent, we need to set `tableIDSize` first, but it's a private field.
   472  	// so, we need to use a BinlogParser to parse a FormatDescriptionEvent first.
   473  	formatDescEv, err := GenFormatDescriptionEvent(header, 4)
   474  	if err != nil {
   475  		return nil, terror.Annotate(err, "generate FormatDescriptionEvent")
   476  	}
   477  
   478  	var tableMapEvent *replication.BinlogEvent
   479  	count := 0
   480  	onEventFunc := func(e *replication.BinlogEvent) error {
   481  		count++
   482  		switch count {
   483  		case 1: // FormatDescriptionEvent
   484  			if e.Header.EventType != replication.FORMAT_DESCRIPTION_EVENT {
   485  				return terror.ErrBinlogExpectFormatDescEv.Generate(e)
   486  			}
   487  		case 2: // TableMapEvent
   488  			if e.Header.EventType != replication.TABLE_MAP_EVENT {
   489  				return terror.ErrBinlogExpectTableMapEv.Generate(e)
   490  			}
   491  			tableMapEvent = e
   492  		default:
   493  			return terror.ErrBinlogUnexpectedEv.Generate(e)
   494  		}
   495  		return nil
   496  	}
   497  
   498  	parse2 := replication.NewBinlogParser()
   499  	parse2.SetVerifyChecksum(true)
   500  	// parse FormatDescriptionEvent
   501  	_, err = parse2.ParseSingleEvent(bytes.NewReader(formatDescEv.RawData), onEventFunc)
   502  	if err != nil {
   503  		return nil, terror.ErrBinlogParseSingleEv.AnnotateDelegate(err, "parse FormatDescriptionEvent % X", formatDescEv.RawData)
   504  	}
   505  
   506  	// parse TableMapEvent
   507  	_, err = parse2.ParseSingleEvent(bytes.NewReader(buf.Bytes()), onEventFunc)
   508  	if err != nil {
   509  		return nil, terror.ErrBinlogParseSingleEv.AnnotateDelegate(err, "parse TableMapEvent % X", buf.Bytes())
   510  	}
   511  
   512  	return tableMapEvent, nil
   513  }
   514  
   515  // GenRowsEvent generates a RowsEvent.
   516  // RowsEvent includes:
   517  //
   518  //	WRITE_ROWS_EVENTv0, WRITE_ROWS_EVENTv1, WRITE_ROWS_EVENTv2
   519  //	UPDATE_ROWS_EVENTv0, UPDATE_ROWS_EVENTv1, UPDATE_ROWS_EVENTv2
   520  //	DELETE_ROWS_EVENTv0, DELETE_ROWS_EVENTv1, DELETE_ROWS_EVENTv2
   521  //
   522  // ref: https://dev.mysql.com/doc/internals/en/rows-event.html
   523  // ref: http://blog.51cto.com/yanzongshuai/2090894
   524  func GenRowsEvent(header *replication.EventHeader, latestPos uint32, eventType replication.EventType, tableID uint64, rowsFlags uint16, rows [][]interface{}, columnType []byte, tableMapEv *replication.BinlogEvent) (*replication.BinlogEvent, error) {
   525  	switch eventType {
   526  	case replication.WRITE_ROWS_EVENTv0, replication.WRITE_ROWS_EVENTv1, replication.WRITE_ROWS_EVENTv2,
   527  		replication.UPDATE_ROWS_EVENTv0, replication.UPDATE_ROWS_EVENTv1, replication.UPDATE_ROWS_EVENTv2,
   528  		replication.DELETE_ROWS_EVENTv0, replication.DELETE_ROWS_EVENTv1, replication.DELETE_ROWS_EVENTv2:
   529  	default:
   530  		return nil, terror.ErrBinlogEventTypeNotValid.Generate(eventType)
   531  	}
   532  
   533  	if len(rows) == 0 {
   534  		return nil, terror.ErrBinlogEventNoRows.Generate()
   535  	}
   536  	if len(columnType) == 0 {
   537  		return nil, terror.ErrBinlogEventNoColumns.Generate()
   538  	}
   539  	for _, row := range rows {
   540  		if len(row) != len(columnType) {
   541  			// all rows have the same length (no nil), and equal to the length of column-type
   542  			return nil, terror.ErrBinlogEventRowLengthNotEq.Generate(len(row), len(columnType))
   543  		}
   544  	}
   545  
   546  	postHeader := new(bytes.Buffer)
   547  
   548  	tableIDSize := 6
   549  	if eventTypeHeaderLen[eventType] == 6 {
   550  		tableIDSize = 4
   551  	}
   552  	// table id, tableIDSize bytes
   553  	err := binary.Write(postHeader, binary.LittleEndian, tableID)
   554  	if err != nil {
   555  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write table id %d", tableID)
   556  	}
   557  	postHeader.Truncate(tableIDSize) // truncate the unwanted bytes
   558  
   559  	// flags, 2 bytes
   560  	err = binary.Write(postHeader, binary.LittleEndian, rowsFlags)
   561  	if err != nil {
   562  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write flags %d", rowsFlags)
   563  	}
   564  
   565  	// extra-data
   566  	switch eventType {
   567  	case replication.WRITE_ROWS_EVENTv2, replication.UPDATE_ROWS_EVENTv2, replication.DELETE_ROWS_EVENTv2:
   568  		// if version=2, extra data exist.
   569  		// NOTE: we do not support to write any meaningful extra data yet.
   570  		var extraDataLen uint16 = 2 // two bytes, but with value `2` (no extra data, only this len variable)
   571  		err = binary.Write(postHeader, binary.LittleEndian, extraDataLen)
   572  		if err != nil {
   573  			return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write extra data length %d", extraDataLen)
   574  		}
   575  	default:
   576  	}
   577  
   578  	payload := new(bytes.Buffer)
   579  
   580  	// number of columns, lenenc-int
   581  	columnCount := gmysql.PutLengthEncodedInt(uint64(len(rows[0])))
   582  	err = binary.Write(payload, binary.LittleEndian, columnCount)
   583  	if err != nil {
   584  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write number of columns % X", columnCount)
   585  	}
   586  
   587  	// columns-present-bitmap1, (num of columns+7)/8 bytes
   588  	byteCount := bitmapByteSize(len(rows[0]))
   589  	bitmap := fullBytes(byteCount) // NOTE: only support to write full columns now
   590  	err = binary.Write(payload, binary.LittleEndian, bitmap)
   591  	if err != nil {
   592  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write columns-present-bitmap1 % X", bitmap)
   593  	}
   594  
   595  	isUpdateV1V2 := eventType == replication.UPDATE_ROWS_EVENTv1 || eventType == replication.UPDATE_ROWS_EVENTv2
   596  	if isUpdateV1V2 {
   597  		// NOTE: use columns-present-bitmap1 as columns-present-bitmap2 now
   598  		err = binary.Write(payload, binary.LittleEndian, bitmap)
   599  		if err != nil {
   600  			return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write columns-present-bitmap2 % X", bitmap)
   601  		}
   602  	}
   603  
   604  	columnMetaData, err := encodeTableMapColumnMeta(columnType)
   605  	if err != nil {
   606  		return nil, terror.Annotatef(err, "encode column-meta-def from column-type % X", columnType)
   607  	}
   608  	columnMeta, err := decodeTableMapColumnMeta(columnMetaData, columnType)
   609  	if err != nil {
   610  		return nil, terror.Annotatef(err, "decode column-meta-def %X", columnMetaData)
   611  	}
   612  
   613  	// for UPDATE, two rows for one statement
   614  	// currently, columns-present-bitmap2 is just columns-present-bitmap1
   615  	for _, row := range rows {
   616  		// nul-bitmap, (num of columns+7)/8 bytes
   617  		nulBitmap := nullBytes(byteCount) // NOTE: no column with nil value now
   618  		err = binary.Write(payload, binary.LittleEndian, nulBitmap)
   619  		if err != nil {
   620  			return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write nul-bitmap % X for row %v", nulBitmap, row)
   621  		}
   622  
   623  		for i, col := range row {
   624  			var colData []byte
   625  			colData, err = encodeColumnValue(col, columnType[i], columnMeta[i])
   626  			if err != nil {
   627  				return nil, terror.Annotatef(err, "encode column value %v to bytes", col)
   628  			}
   629  			err = binary.Write(payload, binary.LittleEndian, colData)
   630  			if err != nil {
   631  				return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write column data % X", colData)
   632  			}
   633  		}
   634  	}
   635  
   636  	buf := new(bytes.Buffer)
   637  	_, err = assembleEvent(buf, nil, false, *header, eventType, latestPos, postHeader.Bytes(), payload.Bytes())
   638  	if err != nil {
   639  		return nil, err
   640  	}
   641  
   642  	// in order to Decode RowsEvent, we need to set `tableIDSize` and `tables` first, but they are private fields.
   643  	// so we should parse a FormatDescriptionEvent and a TableMapEvent first.
   644  	var rowsEvent *replication.BinlogEvent
   645  	count := 0
   646  	onEventFunc := func(e *replication.BinlogEvent) error {
   647  		count++
   648  		switch count {
   649  		case 1: // FormatDescriptionEvent
   650  			if e.Header.EventType != replication.FORMAT_DESCRIPTION_EVENT {
   651  				return terror.ErrBinlogExpectFormatDescEv.Generate(e)
   652  			}
   653  		case 2: // TableMapEvent
   654  			if e.Header.EventType != replication.TABLE_MAP_EVENT {
   655  				return terror.ErrBinlogExpectTableMapEv.Generate(e)
   656  			}
   657  		case 3: // RowsEvent
   658  			if e.Header.EventType != eventType {
   659  				return terror.ErrBinlogExpectRowsEv.Generate(eventType, e)
   660  			}
   661  			rowsEvent = e
   662  		default:
   663  			return terror.ErrBinlogUnexpectedEv.Generate(e)
   664  		}
   665  		return nil
   666  	}
   667  
   668  	parse2 := replication.NewBinlogParser()
   669  	parse2.SetVerifyChecksum(true)
   670  
   671  	// parse FormatDescriptionEvent
   672  	formatDescEv, err := GenFormatDescriptionEvent(header, 4)
   673  	if err != nil {
   674  		return nil, terror.Annotate(err, "generate FormatDescriptionEvent")
   675  	}
   676  	_, err = parse2.ParseSingleEvent(bytes.NewReader(formatDescEv.RawData), onEventFunc)
   677  	if err != nil {
   678  		return nil, terror.ErrBinlogParseSingleEv.AnnotateDelegate(err, "parse FormatDescriptionEvent % X", formatDescEv.RawData)
   679  	}
   680  
   681  	// parse TableMapEvent
   682  	if tableMapEv == nil {
   683  		tableMapEv, err = GenTableMapEvent(header, latestPos, tableID, []byte("schema-placeholder"), []byte("table-placeholder"), columnType)
   684  		if err != nil {
   685  			return nil, terror.Annotate(err, "generate TableMapEvent")
   686  		}
   687  	}
   688  	_, err = parse2.ParseSingleEvent(bytes.NewReader(tableMapEv.RawData), onEventFunc)
   689  	if err != nil {
   690  		return nil, terror.ErrBinlogParseSingleEv.AnnotateDelegate(err, "parse TableMapEvent % x", tableMapEv.RawData)
   691  	}
   692  
   693  	// parse RowsEvent
   694  	_, err = parse2.ParseSingleEvent(bytes.NewReader(buf.Bytes()), onEventFunc)
   695  	if err != nil {
   696  		return nil, terror.ErrBinlogParseSingleEv.AnnotateDelegate(err, "parse RowsEvent % X", buf.Bytes())
   697  	}
   698  
   699  	return rowsEvent, nil
   700  }
   701  
   702  // GenXIDEvent generates a XIDEvent.
   703  // ref: https://dev.mysql.com/doc/internals/en/xid-event.html
   704  func GenXIDEvent(header *replication.EventHeader, latestPos uint32, xid uint64) (*replication.BinlogEvent, error) {
   705  	// Payload
   706  	payload := new(bytes.Buffer)
   707  	err := binary.Write(payload, binary.LittleEndian, xid)
   708  	if err != nil {
   709  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write XID %d", xid)
   710  	}
   711  
   712  	buf := new(bytes.Buffer)
   713  	event := &replication.XIDEvent{}
   714  	ev, err := assembleEvent(buf, event, false, *header, replication.XID_EVENT, latestPos, nil, payload.Bytes())
   715  	return ev, err
   716  }
   717  
   718  // GenMariaDBGTIDListEvent generates a MariadbGTIDListEvent.
   719  // ref: https://mariadb.com/kb/en/library/gtid_list_event/
   720  func GenMariaDBGTIDListEvent(header *replication.EventHeader, latestPos uint32, gSet gmysql.GTIDSet) (*replication.BinlogEvent, error) {
   721  	if gtid.CheckGTIDSetEmpty(gSet) {
   722  		return nil, terror.ErrBinlogEmptyGTID.Generate()
   723  	}
   724  
   725  	mariaDBGSet, ok := gSet.(*gmysql.MariadbGTIDSet)
   726  	if !ok {
   727  		return nil, terror.ErrBinlogGTIDMariaDBNotValid.Generate(gSet.String())
   728  	}
   729  
   730  	payload := new(bytes.Buffer)
   731  
   732  	// Number of GTIDs, 4 bytes
   733  	numOfGTIDs := uint32(0)
   734  	for _, set := range mariaDBGSet.Sets {
   735  		numOfGTIDs += uint32(len(set))
   736  	}
   737  	err := binary.Write(payload, binary.LittleEndian, numOfGTIDs)
   738  	if err != nil {
   739  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write Number of GTIDs %d", numOfGTIDs)
   740  	}
   741  
   742  	for _, set := range mariaDBGSet.Sets {
   743  		for _, mGTID := range set {
   744  			// Replication Domain ID, 4 bytes
   745  			err = binary.Write(payload, binary.LittleEndian, mGTID.DomainID)
   746  			if err != nil {
   747  				return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write Replication Domain ID %d", mGTID.DomainID)
   748  			}
   749  			// Server_ID, 4 bytes
   750  			err = binary.Write(payload, binary.LittleEndian, mGTID.ServerID)
   751  			if err != nil {
   752  				return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write Server_ID %d", mGTID.ServerID)
   753  			}
   754  			// GTID sequence, 8 bytes
   755  			err = binary.Write(payload, binary.LittleEndian, mGTID.SequenceNumber)
   756  			if err != nil {
   757  				return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write GTID sequence %d", mGTID.SequenceNumber)
   758  			}
   759  		}
   760  	}
   761  
   762  	buf := new(bytes.Buffer)
   763  	event := &replication.MariadbGTIDListEvent{}
   764  	ev, err := assembleEvent(buf, event, false, *header, replication.MARIADB_GTID_LIST_EVENT, latestPos, nil, payload.Bytes())
   765  	return ev, err
   766  }
   767  
   768  // GenMariaDBGTIDEvent generates a MariadbGTIDEvent.
   769  // ref: https://mariadb.com/kb/en/library/gtid_event/
   770  func GenMariaDBGTIDEvent(header *replication.EventHeader, latestPos uint32, sequenceNum uint64, domainID uint32) (*replication.BinlogEvent, error) {
   771  	payload := new(bytes.Buffer)
   772  
   773  	// GTID sequence, 8 bytes
   774  	err := binary.Write(payload, binary.LittleEndian, sequenceNum)
   775  	if err != nil {
   776  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write GTID sequence %d", sequenceNum)
   777  	}
   778  
   779  	// Replication Domain ID, 4 bytes
   780  	err = binary.Write(payload, binary.LittleEndian, domainID)
   781  	if err != nil {
   782  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write Replication Domain ID %d", domainID)
   783  	}
   784  
   785  	// Flags, 1 byte, keep zero now.
   786  	xidFlags := uint8(0x00)
   787  	err = binary.Write(payload, binary.LittleEndian, xidFlags)
   788  	if err != nil {
   789  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write Flags %d", xidFlags)
   790  	}
   791  
   792  	// "if flag & FL_GROUP_COMMIT_ID" is FALSE
   793  	// commit_id, 6 bytes with zero value
   794  	err = binary.Write(payload, binary.LittleEndian, uint64(0x00))
   795  	if err != nil {
   796  		return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write 6 bytes commit_id with zero value")
   797  	}
   798  	payload.Truncate(payload.Len() - 2) // len(uint64) - 2 == 6 bytes
   799  
   800  	buf := new(bytes.Buffer)
   801  	event := &replication.MariadbGTIDEvent{}
   802  	ev, err := assembleEvent(buf, event, false, *header, replication.MARIADB_GTID_EVENT, latestPos, nil, payload.Bytes())
   803  	return ev, err
   804  }
   805  
   806  // GenDummyEvent generates a dummy QueryEvent or a dummy USER_VAR_EVENT.
   807  // Dummy events often used to fill the holes in a relay log file which lacking some events from the master.
   808  // The minimum size is 29 bytes (19 bytes header + 6 bytes body for a USER_VAR_EVENT + 4 bytes checksum).
   809  // ref: https://dev.mysql.com/doc/internals/en/user-var-event.html
   810  // ref: https://github.com/MariaDB/server/blob/a765b19e5ca31a3d866cdbc8bef3a6f4e5e44688/sql/log_event.cc#L4950
   811  func GenDummyEvent(header *replication.EventHeader, latestPos uint32, eventSize uint32) (*replication.BinlogEvent, error) {
   812  	if eventSize < MinUserVarEventLen {
   813  		return nil, terror.ErrBinlogDummyEvSizeTooSmall.Generate(eventSize, MinUserVarEventLen)
   814  	}
   815  
   816  	// modify flag in the header
   817  	headerClone := *header // do a copy
   818  	headerClone.Flags &= ^replication.LOG_EVENT_THREAD_SPECIFIC_F
   819  	headerClone.Flags |= replication.LOG_EVENT_SUPPRESS_USE_F
   820  	headerClone.Flags |= replication.LOG_EVENT_RELAY_LOG_F // now, the dummy event created by relay only
   821  
   822  	if eventSize < MinQueryEventLen {
   823  		// generate a USER_VAR_EVENT
   824  		var (
   825  			payload   = new(bytes.Buffer)
   826  			buf       = new(bytes.Buffer)
   827  			event     = &replication.GenericEvent{}
   828  			eventType = replication.USER_VAR_EVENT
   829  			nameLen   = eventSize - (MinUserVarEventLen - 1)
   830  			nameBytes = make([]byte, nameLen)
   831  		)
   832  		copy(nameBytes, dummyUserVarName)
   833  		// name_length, 4 bytes
   834  		err := binary.Write(payload, binary.LittleEndian, nameLen)
   835  		if err != nil {
   836  			return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write USER_VAR_EVENT name length %d", nameLen)
   837  		}
   838  		// name, name_length bytes (now, at least 1 byte)
   839  		err = binary.Write(payload, binary.LittleEndian, nameBytes)
   840  		if err != nil {
   841  			return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write USER_VAR_EVENT name % X", nameBytes)
   842  		}
   843  		// is_null, 1 byte
   844  		isNull := byte(1) // always is null (no `value` part)
   845  		err = binary.Write(payload, binary.LittleEndian, isNull)
   846  		if err != nil {
   847  			return nil, terror.ErrBinlogWriteBinaryData.AnnotateDelegate(err, "write USER_VAR_EVENT is-null % X", isNull)
   848  		}
   849  		ev, err := assembleEvent(buf, event, false, headerClone, eventType, latestPos, nil, payload.Bytes())
   850  		return ev, err
   851  	}
   852  
   853  	// generate a QueryEvent
   854  	queryLen := eventSize - (MinQueryEventLen - 1)
   855  	queryBytes := make([]byte, queryLen)
   856  	copy(queryBytes, dummyQuery)
   857  	ev, err := GenQueryEvent(&headerClone, latestPos, 0, 0, 0, nil, nil, queryBytes)
   858  	return ev, err
   859  }
   860  
   861  // GenHeartbeatEvent generates a heartbeat event.
   862  // ref: https://dev.mysql.com/doc/internals/en/heartbeat-event.html
   863  func GenHeartbeatEvent(header *replication.EventHeader) *replication.BinlogEvent {
   864  	// modify header
   865  	headerClone := *header // do a copy
   866  	headerClone.Flags = 0
   867  	headerClone.EventSize = 39
   868  	headerClone.Timestamp = 0
   869  	headerClone.EventType = replication.HEARTBEAT_EVENT
   870  
   871  	eventBytes := make([]byte, 39)
   872  	ev := &replication.BinlogEvent{Header: &headerClone, Event: &replication.GenericEvent{Data: eventBytes}}
   873  
   874  	return ev
   875  }