vitess.io/vitess@v0.16.2/go/mysql/binlog_event_common.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package mysql 18 19 import ( 20 "bytes" 21 "encoding/binary" 22 23 binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" 24 "vitess.io/vitess/go/vt/proto/vtrpc" 25 "vitess.io/vitess/go/vt/vterrors" 26 ) 27 28 // binlogEvent wraps a raw packet buffer and provides methods to examine it 29 // by partially implementing BinlogEvent. These methods can be composed 30 // into flavor-specific event types to pull in common parsing code. 31 // 32 // The default v4 header format is: 33 // 34 // offset : size 35 // +============================+ 36 // | timestamp 0 : 4 | 37 // +----------------------------+ 38 // | type_code 4 : 1 | 39 // +----------------------------+ 40 // | server_id 5 : 4 | 41 // +----------------------------+ 42 // | event_length 9 : 4 | 43 // +----------------------------+ 44 // | next_position 13 : 4 | 45 // +----------------------------+ 46 // | flags 17 : 2 | 47 // +----------------------------+ 48 // | extra_headers 19 : x-19 | 49 // +============================+ 50 // http://dev.mysql.com/doc/internals/en/event-header-fields.html 51 type binlogEvent []byte 52 53 // dataBytes returns the event bytes without header prefix and without checksum suffix 54 func (ev binlogEvent) dataBytes(f BinlogFormat) []byte { 55 data := ev.Bytes()[f.HeaderLength:] 56 data = data[0 : len(data)-4] 57 return data 58 } 59 60 // IsValid implements BinlogEvent.IsValid(). 61 func (ev binlogEvent) IsValid() bool { 62 bufLen := len(ev.Bytes()) 63 64 // The buffer must be at least 19 bytes to contain a valid header. 65 if bufLen < 19 { 66 return false 67 } 68 69 // It's now safe to use methods that examine header fields. 70 // Let's see if the event is right about its own size. 71 evLen := ev.Length() 72 if evLen < 19 || evLen != uint32(bufLen) { 73 return false 74 } 75 76 // Everything's there, so we shouldn't have any out-of-bounds issues while 77 // reading header fields or constant-offset data fields. We should still check 78 // bounds any time we compute an offset based on values in the buffer itself. 79 return true 80 } 81 82 // Bytes returns the underlying byte buffer. 83 func (ev binlogEvent) Bytes() []byte { 84 return []byte(ev) 85 } 86 87 // Type returns the type_code field from the header. 88 func (ev binlogEvent) Type() byte { 89 return ev.Bytes()[4] 90 } 91 92 // Flags returns the flags field from the header. 93 func (ev binlogEvent) Flags() uint16 { 94 return binary.LittleEndian.Uint16(ev.Bytes()[17 : 17+2]) 95 } 96 97 // Timestamp returns the timestamp field from the header. 98 func (ev binlogEvent) Timestamp() uint32 { 99 return binary.LittleEndian.Uint32(ev.Bytes()[:4]) 100 } 101 102 // ServerID returns the server_id field from the header. 103 func (ev binlogEvent) ServerID() uint32 { 104 return binary.LittleEndian.Uint32(ev.Bytes()[5 : 5+4]) 105 } 106 107 // Length returns the event_length field from the header. 108 func (ev binlogEvent) Length() uint32 { 109 return binary.LittleEndian.Uint32(ev.Bytes()[9 : 9+4]) 110 } 111 112 // NextPosition returns the nextPosition field from the header 113 func (ev binlogEvent) NextPosition() uint32 { 114 return binary.LittleEndian.Uint32(ev.Bytes()[13 : 13+4]) 115 } 116 117 // IsFormatDescription implements BinlogEvent.IsFormatDescription(). 118 func (ev binlogEvent) IsFormatDescription() bool { 119 return ev.Type() == eFormatDescriptionEvent 120 } 121 122 // IsQuery implements BinlogEvent.IsQuery(). 123 func (ev binlogEvent) IsQuery() bool { 124 return ev.Type() == eQueryEvent 125 } 126 127 // IsRotate implements BinlogEvent.IsRotate(). 128 func (ev binlogEvent) IsRotate() bool { 129 return ev.Type() == eRotateEvent 130 } 131 132 // IsXID implements BinlogEvent.IsXID(). 133 func (ev binlogEvent) IsXID() bool { 134 return ev.Type() == eXIDEvent 135 } 136 137 // IsStop implements BinlogEvent.IsStop(). 138 func (ev binlogEvent) IsStop() bool { 139 return ev.Type() == eStopEvent 140 } 141 142 // IsIntVar implements BinlogEvent.IsIntVar(). 143 func (ev binlogEvent) IsIntVar() bool { 144 return ev.Type() == eIntVarEvent 145 } 146 147 // IsRand implements BinlogEvent.IsRand(). 148 func (ev binlogEvent) IsRand() bool { 149 return ev.Type() == eRandEvent 150 } 151 152 // IsPreviousGTIDs implements BinlogEvent.IsPreviousGTIDs(). 153 func (ev binlogEvent) IsPreviousGTIDs() bool { 154 return ev.Type() == ePreviousGTIDsEvent 155 } 156 157 // IsHeartbeat implements BinlogEvent.IsHeartbeat(). 158 func (ev binlogEvent) IsHeartbeat() bool { 159 return ev.Type() == eHeartbeatEvent 160 } 161 162 // IsTableMap implements BinlogEvent.IsTableMap(). 163 func (ev binlogEvent) IsTableMap() bool { 164 return ev.Type() == eTableMapEvent 165 } 166 167 // IsWriteRows implements BinlogEvent.IsWriteRows(). 168 // We do not support v0. 169 func (ev binlogEvent) IsWriteRows() bool { 170 return ev.Type() == eWriteRowsEventV1 || 171 ev.Type() == eWriteRowsEventV2 172 } 173 174 // IsUpdateRows implements BinlogEvent.IsUpdateRows(). 175 // We do not support v0. 176 func (ev binlogEvent) IsUpdateRows() bool { 177 return ev.Type() == eUpdateRowsEventV1 || 178 ev.Type() == eUpdateRowsEventV2 179 } 180 181 // IsDeleteRows implements BinlogEvent.IsDeleteRows(). 182 // We do not support v0. 183 func (ev binlogEvent) IsDeleteRows() bool { 184 return ev.Type() == eDeleteRowsEventV1 || 185 ev.Type() == eDeleteRowsEventV2 186 } 187 188 // IsPseudo is always false for a native binlogEvent. 189 func (ev binlogEvent) IsPseudo() bool { 190 return false 191 } 192 193 // IsCompressed returns true if a compressed event is found (binlog_transaction_compression=ON) 194 func (ev binlogEvent) IsCompressed() bool { 195 return ev.Type() == eCompressedEvent 196 } 197 198 // Format implements BinlogEvent.Format(). 199 // 200 // Expected format (L = total length of event data): 201 // 202 // # bytes field 203 // 2 format version 204 // 50 server version string, 0-padded but not necessarily 0-terminated 205 // 4 timestamp (same as timestamp header field) 206 // 1 header length 207 // p (one byte per packet type) event type header lengths 208 // Rest was inferred from reading source code: 209 // 1 checksum algorithm 210 // 4 checksum 211 func (ev binlogEvent) Format() (f BinlogFormat, err error) { 212 // FORMAT_DESCRIPTION_EVENT has a fixed header size of 19 213 // because we have to read it before we know the header_length. 214 data := ev.Bytes()[19:] 215 216 f.FormatVersion = binary.LittleEndian.Uint16(data[:2]) 217 if f.FormatVersion != 4 { 218 return f, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "format version = %d, we only support version 4", f.FormatVersion) 219 } 220 f.ServerVersion = string(bytes.TrimRight(data[2:2+50], "\x00")) 221 f.HeaderLength = data[2+50+4] 222 if f.HeaderLength < 19 { 223 return f, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "header length = %d, should be >= 19", f.HeaderLength) 224 } 225 226 // MySQL/MariaDB 5.6.1+ always adds a 4-byte checksum to the end of a 227 // FORMAT_DESCRIPTION_EVENT, regardless of the server setting. The byte 228 // immediately before that checksum tells us which checksum algorithm 229 // (if any) is used for the rest of the events. 230 f.ChecksumAlgorithm = data[len(data)-5] 231 232 f.HeaderSizes = data[2+50+4+1 : len(data)-5] 233 return f, nil 234 } 235 236 // Query implements BinlogEvent.Query(). 237 // 238 // Expected format (L = total length of event data): 239 // 240 // # bytes field 241 // 4 thread_id 242 // 4 execution time 243 // 1 length of db_name, not including NULL terminator (X) 244 // 2 error code 245 // 2 length of status vars block (Y) 246 // Y status vars block 247 // X+1 db_name + NULL terminator 248 // L-X-1-Y SQL statement (no NULL terminator) 249 func (ev binlogEvent) Query(f BinlogFormat) (query Query, err error) { 250 const varsPos = 4 + 4 + 1 + 2 + 2 251 252 data := ev.Bytes()[f.HeaderLength:] 253 254 // length of database name 255 dbLen := int(data[4+4]) 256 // length of status variables block 257 varsLen := int(binary.LittleEndian.Uint16(data[4+4+1+2 : 4+4+1+2+2])) 258 259 // position of database name 260 dbPos := varsPos + varsLen 261 // position of SQL query 262 sqlPos := dbPos + dbLen + 1 // +1 for NULL terminator 263 if sqlPos > len(data) { 264 return query, vterrors.Errorf(vtrpc.Code_INTERNAL, "SQL query position overflows buffer (%v > %v)", sqlPos, len(data)) 265 } 266 267 // We've checked that the buffer is big enough for sql, so everything before 268 // it (db and vars) is in-bounds too. 269 query.Database = string(data[dbPos : dbPos+dbLen]) 270 query.SQL = string(data[sqlPos:]) 271 272 // Scan the status vars for ones we care about. This requires us to know the 273 // size of every var that comes before the ones we're interested in. 274 vars := data[varsPos : varsPos+varsLen] 275 276 varsLoop: 277 for pos := 0; pos < len(vars); { 278 code := vars[pos] 279 pos++ 280 281 // All codes are optional, but if present they must occur in numerically 282 // increasing order (except for 6 which occurs in the place of 2) to allow 283 // for backward compatibility. 284 switch code { 285 case QFlags2Code, QAutoIncrement: 286 pos += 4 287 case QSQLModeCode: 288 pos += 8 289 case QCatalog: // Used in MySQL 5.0.0 - 5.0.3 290 if pos+1 > len(vars) { 291 return query, vterrors.Errorf(vtrpc.Code_INTERNAL, "Q_CATALOG status var overflows buffer (%v + 1 > %v)", pos, len(vars)) 292 } 293 pos += 1 + int(vars[pos]) + 1 294 case QCatalogNZCode: // Used in MySQL > 5.0.3 to replace QCatalog 295 if pos+1 > len(vars) { 296 return query, vterrors.Errorf(vtrpc.Code_INTERNAL, "Q_CATALOG_NZ_CODE status var overflows buffer (%v + 1 > %v)", pos, len(vars)) 297 } 298 pos += 1 + int(vars[pos]) 299 case QCharsetCode: 300 if pos+6 > len(vars) { 301 return query, vterrors.Errorf(vtrpc.Code_INTERNAL, "Q_CHARSET_CODE status var overflows buffer (%v + 6 > %v)", pos, len(vars)) 302 } 303 query.Charset = &binlogdatapb.Charset{ 304 Client: int32(binary.LittleEndian.Uint16(vars[pos : pos+2])), 305 Conn: int32(binary.LittleEndian.Uint16(vars[pos+2 : pos+4])), 306 Server: int32(binary.LittleEndian.Uint16(vars[pos+4 : pos+6])), 307 } 308 pos += 6 309 default: 310 // If we see something higher than what we're interested in, we can stop. 311 break varsLoop 312 } 313 } 314 315 return query, nil 316 } 317 318 // IntVar implements BinlogEvent.IntVar(). 319 // 320 // Expected format (L = total length of event data): 321 // 322 // # bytes field 323 // 1 variable ID 324 // 8 variable value 325 func (ev binlogEvent) IntVar(f BinlogFormat) (byte, uint64, error) { 326 data := ev.Bytes()[f.HeaderLength:] 327 328 typ := data[0] 329 if typ != IntVarLastInsertID && typ != IntVarInsertID { 330 return 0, 0, vterrors.Errorf(vtrpc.Code_INTERNAL, "invalid IntVar ID: %v", data[0]) 331 } 332 333 value := binary.LittleEndian.Uint64(data[1 : 1+8]) 334 return typ, value, nil 335 } 336 337 // Rand implements BinlogEvent.Rand(). 338 // 339 // Expected format (L = total length of event data): 340 // 341 // # bytes field 342 // 8 seed 1 343 // 8 seed 2 344 func (ev binlogEvent) Rand(f BinlogFormat) (seed1 uint64, seed2 uint64, err error) { 345 data := ev.Bytes()[f.HeaderLength:] 346 seed1 = binary.LittleEndian.Uint64(data[0:8]) 347 seed2 = binary.LittleEndian.Uint64(data[8 : 8+8]) 348 return seed1, seed2, nil 349 } 350 351 func (ev binlogEvent) TableID(f BinlogFormat) uint64 { 352 typ := ev.Type() 353 pos := f.HeaderLength 354 if f.HeaderSize(typ) == 6 { 355 // Encoded in 4 bytes. 356 return uint64(binary.LittleEndian.Uint32(ev[pos : pos+4])) 357 } 358 359 // Encoded in 6 bytes. 360 return uint64(ev[pos]) | 361 uint64(ev[pos+1])<<8 | 362 uint64(ev[pos+2])<<16 | 363 uint64(ev[pos+3])<<24 | 364 uint64(ev[pos+4])<<32 | 365 uint64(ev[pos+5])<<40 366 } 367 368 func (ev binlogEvent) NextLogFile(f BinlogFormat) (string, uint64, error) { 369 data := ev.dataBytes(f) 370 pos := 0 371 logPos, pos, _ := readUint64(data, pos) 372 logFile := string(data[pos:]) 373 return logFile, logPos, nil 374 }