github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/pkg/binlog/event/dml.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  package event
    15  
    16  import (
    17  	"bytes"
    18  	"time"
    19  
    20  	"github.com/go-mysql-org/go-mysql/mysql"
    21  	"github.com/go-mysql-org/go-mysql/replication"
    22  	"github.com/pingcap/tiflow/dm/pkg/terror"
    23  )
    24  
    25  // DMLData represents data used to generate events for DML statements.
    26  type DMLData struct {
    27  	TableID    uint64
    28  	Schema     string
    29  	Table      string
    30  	ColumnType []byte
    31  	Rows       [][]interface{}
    32  
    33  	// if Query is not empty, we generate a Query event
    34  	Query string
    35  }
    36  
    37  // GenDMLEvents generates binlog events for `INSERT`/`UPDATE`/`DELETE`.
    38  // if DMLData.Query is empty:
    39  //
    40  //		 events: [GTIDEvent, QueryEvent, TableMapEvent, RowsEvent, ..., XIDEvent]
    41  //	  NOTE: multi <TableMapEvent, RowsEvent> pairs can be in events.
    42  //
    43  // if DMLData.Query is not empty:
    44  //
    45  //		 events: [GTIDEvent, QueryEvent, QueryEvent, ..., XIDEvent]
    46  //	  NOTE: multi <QueryEvent> can be in events.
    47  func GenDMLEvents(flavor string, serverID uint32, latestPos uint32, latestGTID mysql.GTIDSet, eventType replication.EventType, xid uint64, dmlData []*DMLData, genGTID, anonymousGTID bool, ts int64) (*DDLDMLResult, error) {
    48  	if len(dmlData) == 0 {
    49  		return nil, terror.ErrBinlogDMLEmptyData.Generate()
    50  	}
    51  
    52  	if ts == 0 {
    53  		ts = time.Now().Unix()
    54  	}
    55  
    56  	// GTIDEvent, increase GTID first.
    57  	latestGTID, err := GTIDIncrease(flavor, latestGTID)
    58  	if err != nil {
    59  		return nil, terror.Annotatef(err, "increase GTID %s", latestGTID)
    60  	}
    61  	var gtidEv *replication.BinlogEvent
    62  	if genGTID {
    63  		gtidEv, err = GenCommonGTIDEvent(flavor, serverID, latestPos, latestGTID, anonymousGTID, ts)
    64  		if err != nil {
    65  			return nil, terror.Annotate(err, "generate GTIDEvent")
    66  		}
    67  		latestPos = gtidEv.Header.LogPos
    68  	}
    69  
    70  	// QueryEvent, `BEGIN`
    71  	header := &replication.EventHeader{
    72  		Timestamp: uint32(ts),
    73  		ServerID:  serverID,
    74  		Flags:     defaultHeaderFlags,
    75  	}
    76  	query := []byte("BEGIN")
    77  	queryEv, err := GenQueryEvent(header, latestPos, defaultSlaveProxyID, defaultExecutionTime, defaultErrorCode, defaultStatusVars, nil, query)
    78  	if err != nil {
    79  		return nil, terror.Annotate(err, "generate QueryEvent for `BEGIN` statement")
    80  	}
    81  	latestPos = queryEv.Header.LogPos
    82  
    83  	// all events
    84  	events := make([]*replication.BinlogEvent, 0, 5)
    85  	if genGTID {
    86  		events = append(events, gtidEv)
    87  	}
    88  	events = append(events, queryEv)
    89  
    90  	// <TableMapEvent, RowsEvent> pairs or QueryEvent
    91  	for _, data := range dmlData {
    92  		if data.Query != "" {
    93  			dmlQueryEv, err2 := GenQueryEvent(header, latestPos, defaultSlaveProxyID, defaultExecutionTime, defaultErrorCode, defaultStatusVars, []byte(data.Schema), []byte(data.Query))
    94  			if err2 != nil {
    95  				return nil, terror.Annotatef(err2, "generate QueryEvent for %s", data.Query)
    96  			}
    97  			latestPos = dmlQueryEv.Header.LogPos
    98  			events = append(events, dmlQueryEv)
    99  			continue
   100  		}
   101  		// TableMapEvent
   102  		tableMapEv, err2 := GenTableMapEvent(header, latestPos, data.TableID, []byte(data.Schema), []byte(data.Table), data.ColumnType)
   103  		if err2 != nil {
   104  			return nil, terror.Annotatef(err2, "generate TableMapEvent for `%s`.`%s`", data.Schema, data.Table)
   105  		}
   106  		latestPos = tableMapEv.Header.LogPos
   107  		events = append(events, tableMapEv)
   108  
   109  		// RowsEvent
   110  		rowsEv, err2 := GenRowsEvent(header, latestPos, eventType, data.TableID, defaultRowsFlag, data.Rows, data.ColumnType, tableMapEv)
   111  		if err2 != nil {
   112  			return nil, terror.Annotatef(err2, "generate RowsEvent for `%s`.`%s`", data.Schema, data.Table)
   113  		}
   114  		latestPos = rowsEv.Header.LogPos
   115  		events = append(events, rowsEv)
   116  	}
   117  
   118  	// XIDEvent
   119  	xidEv, err := GenXIDEvent(header, latestPos, xid)
   120  	if err != nil {
   121  		return nil, terror.Annotatef(err, "generate XIDEvent for %d", xid)
   122  	}
   123  	latestPos = xidEv.Header.LogPos
   124  	events = append(events, xidEv)
   125  
   126  	var buf bytes.Buffer
   127  	for _, ev := range events {
   128  		_, err = buf.Write(ev.RawData)
   129  		if err != nil {
   130  			return nil, terror.ErrBinlogWriteDataToBuffer.AnnotateDelegate(err, "write %d data % X", ev.Header.EventType, ev.RawData)
   131  		}
   132  	}
   133  
   134  	return &DDLDMLResult{
   135  		Events:     events,
   136  		Data:       buf.Bytes(),
   137  		LatestPos:  latestPos,
   138  		LatestGTID: latestGTID,
   139  	}, nil
   140  }