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 }