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  }