func (s *FormatDescriptionEventSuite) Test56FDE(c *C) {
	// This FDE entry was copied from a 5.6 HDB shard.
	s.WriteEvent(
		mysql_proto.LogEventType_FORMAT_DESCRIPTION_EVENT,
		uint16(0),
		[]byte{
			// binlog version
			4, 0,
			// server version
			53, 46, 54, 46, 49, 53, 45, 54, 51, 46,
			48, 45, 108, 111, 103, 0, 0, 0, 0, 0,
			0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
			0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
			0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
			// created timestamp
			0, 0, 0, 0,
			// total header size
			19,
			// fixed length data size per event type
			56, 13, 0, 8, 0, 18, 0, 4, 4, 4, 4, 18, 0, 0, 92, 0, 4, 26,
			8, 0, 0, 0, 8, 8, 8, 2, 0, 0, 0, 10, 10, 10, 25, 25, 0,
			// checksum algorithm
			1,
			// checksum
			40, 216, 52, 169}) // 0x28 0xd8 0x34 0xa9

	event, err := s.NextEvent()
	c.Assert(err, IsNil)

	c.Assert(event, NotNil)
	fde, ok := event.(*FormatDescriptionEvent)
	c.Assert(ok, IsTrue)
	c.Check(fde.BinlogVersion(), Equals, uint16(4))
	c.Check(string(fde.ServerVersion()), Equals, "5.6.15-63.0-log")
	c.Check(fde.CreatedTimestamp(), Equals, uint32(0))
	c.Check(fde.ExtraHeadersSize(), Equals, 0)

	for i := 0; i < int(mysql_proto.LogEventType_PREVIOUS_GTIDS_LOG_EVENT); i++ {
		c.Log(mysql_proto.LogEventType_Type(i).String())
		_, inMap := fde.fixedLengthSizes[mysql_proto.LogEventType_Type(i)]
		c.Check(inMap, IsTrue)
	}

	c.Check(fde.ChecksumAlgorithm(), Equals, mysql_proto.ChecksumAlgorithm_CRC32)
	c.Check(fde.Checksum(), DeepEquals, []byte{40, 216, 52, 169})

	// extra sanity checks
	c.Check(fde.SourceName(), Equals, testSourceName)
	c.Check(
		fde.EventType(),
		Equals,
		mysql_proto.LogEventType_FORMAT_DESCRIPTION_EVENT)

	_, ok = event.(*RawV4Event)
	c.Check(ok, IsFalse)
}
func (s *FormatDescriptionEventSuite) Test55FDE(c *C) {
	// This FDE entry was copied from a 5.5 SFJ shard.
	s.WriteEvent(
		mysql_proto.LogEventType_FORMAT_DESCRIPTION_EVENT,
		uint16(0),
		[]byte{
			// binlog version
			4, 0,
			// server version
			53, 46, 53, 46, 51, 52, 45, 51, 50, 46,
			48, 45, 108, 111, 103, 0, 0, 0, 0, 0,
			0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
			0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
			0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
			// created timestamp
			0, 0, 0, 0,
			// total header size
			19,
			// fixed length data size per event type
			56, 13, 0, 8, 0, 18, 0, 4, 4, 4, 4, 18, 0, 0, 84, 0, 4,
			26, 8, 0, 0, 0, 8, 8, 8, 2, 0})

	event, err := s.NextEvent()
	c.Assert(err, IsNil)

	c.Assert(event, NotNil)
	fde, ok := event.(*FormatDescriptionEvent)
	c.Assert(ok, IsTrue)
	c.Check(fde.BinlogVersion(), Equals, uint16(4))
	c.Check(string(fde.ServerVersion()), Equals, "5.5.34-32.0-log")
	c.Check(fde.CreatedTimestamp(), Equals, uint32(0))
	c.Check(fde.ExtraHeadersSize(), Equals, 0)

	for i := 0; i < int(mysql_proto.LogEventType_HEARTBEAT_LOG_EVENT); i++ {
		c.Log(mysql_proto.LogEventType_Type(i).String())
		_, inMap := fde.fixedLengthSizes[mysql_proto.LogEventType_Type(i)]
		c.Check(inMap, IsTrue)
	}

	for i := int(mysql_proto.LogEventType_IGNORABLE_LOG_EVENT); i < int(mysql_proto.LogEventType_PREVIOUS_GTIDS_LOG_EVENT); i++ {

		c.Log(mysql_proto.LogEventType_Type(i).String())
		_, inMap := fde.fixedLengthSizes[mysql_proto.LogEventType_Type(i)]
		c.Check(inMap, IsFalse)
	}

	c.Check(fde.ChecksumAlgorithm(), Equals, mysql_proto.ChecksumAlgorithm_OFF)
	c.Check(fde.Checksum(), DeepEquals, []byte{})
}
func (h *formatVersionHeader) version() int {
	eventType := mysql_proto.LogEventType_Type(h.EventType)
	if eventType == mysql_proto.LogEventType_FORMAT_DESCRIPTION_EVENT {
		return 4
	} else if eventType == mysql_proto.LogEventType_START_EVENT_V3 {
		if h.EventLength >= maxFirstEventLengthForV1 {
			return 3
		}
		return 1
	}

	return 3
}
func (s *RawV4EventReaderSuite) TestEOFMidHeader(c *C) {
	eventBytes := s.GenerateEvent(
		1, // timestamp
		2, // event type
		3, // server id
		4, // next position
		5, // flags
		2) // data length

	_, err := s.src.Write(eventBytes[:5])
	c.Assert(err, IsNil)

	event, err := s.reader.NextEvent()
	c.Assert(err, Equals, io.EOF)
	c.Check(event, IsNil)

	_, err = s.src.Write(eventBytes[5:13])
	c.Assert(err, IsNil)

	event, err = s.reader.NextEvent()
	c.Assert(err, Equals, io.EOF)
	c.Check(event, IsNil)
	c.Check(s.reader.nextEventEndPosition(), Equals, int64(19))

	// Finish writing the entire event.
	_, err = s.src.Write(eventBytes[13:])
	c.Assert(err, IsNil)

	event, err = s.reader.NextEvent()
	c.Assert(err, IsNil)
	c.Check(event.SourceName(), Equals, testSourceName)
	c.Check(event.SourcePosition(), Equals, int64(0))
	c.Check(event.Timestamp(), Equals, uint32(1))
	c.Check(event.EventType(), Equals, mysql_proto.LogEventType_Type(2))
	c.Check(event.ServerId(), Equals, uint32(3))
	c.Check(event.EventLength(), Equals, uint32(len(eventBytes)))
	c.Check(event.NextPosition(), Equals, uint32(4))
	c.Check(event.Flags(), Equals, uint16(5))
	c.Check(event.ExtraHeaders(), DeepEquals, []byte{})
	c.Check(event.FixedLengthData(), DeepEquals, []byte{})
	c.Check(event.VariableLengthData(), DeepEquals, []byte("\xfe\xfe"))

	c.Check(
		s.reader.nextEventEndPosition(),
		Equals,
		int64(event.EventLength())+int64(19))
}
	if len(raw.FixedLengthData()) != 4 {
		return raw, errors.New("Parse error")
	}
	err := binary.Read(
		bytes.NewReader(raw.FixedLengthData()),
		binary.BigEndian,
		&event.value)
	if err != nil {
		return raw, err
	}

	return event, nil
}

const testRegisteredEventType = mysql_proto.LogEventType_Type(1)
const testParseErrorEventType = mysql_proto.LogEventType_Type(2)
const testInvalidFixedLengthDataSizeEventType = mysql_proto.LogEventType_Type(3)
const testNotRegisteredEventType = mysql_proto.LogEventType_Type(4)
const testFDEEventType = mysql_proto.LogEventType_FORMAT_DESCRIPTION_EVENT // 15

type ParsedV4EventReaderSuite struct {
	src       *bytes.Buffer
	rawReader EventReader
	parsers   V4EventParserMap
	reader    EventReader
}

var _ = Suite(&ParsedV4EventReaderSuite{})

func (s *ParsedV4EventReaderSuite) SetUpTest(c *C) {
// FormatDecriptionEventParser's Parse processes a raw FDE event into a
// FormatDescriptionEvent.
func (p *FormatDescriptionEventParser) Parse(raw *RawV4Event) (Event, error) {
	fde := &FormatDescriptionEvent{
		Event:            raw,
		fixedLengthSizes: make(map[mysql_proto.LogEventType_Type]int),
	}

	data := raw.VariableLengthData()

	data, err := readLittleEndian(data, &fde.binlogVersion)
	if err != nil {
		return raw, errors.Wrap(err, "Failed to read binlog verison")
	}

	serverVersion, data, err := readSlice(data, 50)
	if err != nil {
		return raw, errors.Wrap(err, "Failed to read server version")
	}
	if idx := bytes.IndexByte(serverVersion, byte(0)); idx > -1 {
		serverVersion = serverVersion[:idx]
	}
	fde.serverVersion = serverVersion

	data, err = readLittleEndian(data, &fde.createdTimestamp)
	if err != nil {
		return raw, errors.Wrap(err, "Failed to read created timestamp")
	}

	var totalHeaderSize uint8
	data, err = readLittleEndian(data, &totalHeaderSize)
	if err != nil {
		return raw, errors.Wrap(err, "Failed to read total header size")
	}
	fde.extraHeadersSize = int(totalHeaderSize) - sizeOfBasicV4EventHeader

	numEvents := len(mysql_proto.LogEventType_Type_value)
	hasChecksum := true

	if len(data) == 27 { // mysql 5.5(.37)
		numEvents = 28
		hasChecksum = false
	} else if len(data) == 40 { // mysql 5.6(.17)

		// This is a relay log where the master is 5.5 and slave is 5.6
		if data[int(mysql_proto.LogEventType_WRITE_ROWS_EVENT)-1] == 0 {
			numEvents = 28
		}
	} else {
		return raw, errors.Newf(
			"Unable to parse FDE for mysql variant: %s",
			fde.serverVersion)
	}

	// unknown event's fixed length is implicit.
	fde.fixedLengthSizes[mysql_proto.LogEventType_UNKNOWN_EVENT] = 0
	for i := 1; i < numEvents; i++ {
		fde.fixedLengthSizes[mysql_proto.LogEventType_Type(i)] = int(data[i-1])
	}

	if hasChecksum {
		fde.checksumAlgorithm = mysql_proto.ChecksumAlgorithm_Type(
			data[len(data)-5])

		raw.SetChecksumSize(4)
	}

	return fde, nil
}
예제 #7
0
// EventType returns the event's type.
func (e *RawV4Event) EventType() mysql_proto.LogEventType_Type {
	return mysql_proto.LogEventType_Type(e.header.EventType)
}
func (r *logFileV4EventReader) checkFDE(fde *FormatDescriptionEvent) error {
	if fde.BinlogVersion() != 4 {
		return errors.Newf(
			"Invalid binlog format version: %d",
			fde.BinlogVersion())
	}

	if fde.ExtraHeadersSize() != 0 {
		return errors.Newf(
			"Invalid extra headers size: %d",
			fde.ExtraHeadersSize())
	}

	alg := fde.ChecksumAlgorithm()
	if alg != mysql_proto.ChecksumAlgorithm_OFF &&
		alg != mysql_proto.ChecksumAlgorithm_CRC32 {

		return errors.Newf(
			"Invalid checksum algorithm: %d (%s)",
			alg,
			alg.String())
	}

	errMsg := ""
	for i := 0; i < fde.NumKnownEventTypes(); i++ {
		t := mysql_proto.LogEventType_Type(i)

		if t == mysql_proto.LogEventType_FORMAT_DESCRIPTION_EVENT {
			actual := fde.FixedLengthDataSizeForType(t)
			if actual != FDEFixedLengthDataSizeFor56 &&
				actual != FDEFixedLengthDataSizeFor55 {

				errMsg += fmt.Sprintf(
					"%s (expected: %d (5.6) or %d (5.5) actual: %d); ",
					t.String(),
					FDEFixedLengthDataSizeFor56,
					FDEFixedLengthDataSizeFor55,
					actual)
			}
		} else {
			parser := r.parsers.Get(t)
			if parser == nil {
				continue
			}

			expected := parser.FixedLengthDataSize()
			actual := fde.FixedLengthDataSizeForType(t)
			if expected != actual {
				errMsg += fmt.Sprintf(
					"%s (expected: %d actual: %d); ",
					t.String(),
					expected,
					actual)
			}
		}
	}

	if errMsg != "" {
		return errors.New("Invalid fixed length data size: " + errMsg)
	}

	return nil
}
func (s *RawV4EventReaderSuite) TestParseSimpleEvents(c *C) {
	// hand code a single event just to be sure
	eventBytes1 := []byte(
		"\x04\x03\x02\x01" + // timestamp
			"\x12" + // event type
			"\x0f\x0e\x0e\x0b" + // server id
			"\x16\x00\x00\x00" + // event length (19 header + 3 data)
			"\xf4\xf3\xf2\xf1" + // next position
			"\xad\xde" + // flags
			"\x0a\x0b\x0c") // data

	_, err := s.src.Write(eventBytes1)
	c.Assert(err, IsNil)

	eventBytes2 := s.GenerateEvent(
		0xdeadbeef, // timestamp
		0x69,       // event type
		0xdecafbad, // server id
		0x12345678, // next position
		0xf00d,     // flags
		7)          // data length
	_, err = s.src.Write(eventBytes2)
	c.Assert(err, IsNil)

	eventBytes3 := s.GenerateEvent(
		0x1aaaaaaa, // timestamp
		0x2b,       // event type
		0x3ccccccc, // server id
		0x4ddddddd, // next position
		0x5eee,     // flags
		0)          // data length
	_, err = s.src.Write(eventBytes3)
	c.Assert(err, IsNil)

	event1, err := s.reader.NextEvent()
	c.Assert(err, IsNil)
	c.Check(event1.SourceName(), Equals, testSourceName)
	c.Check(event1.SourcePosition(), Equals, int64(0))
	c.Check(event1.Timestamp(), Equals, uint32(0x01020304))
	c.Check(event1.EventType(), Equals, mysql_proto.LogEventType_Type(0x12))
	c.Check(event1.ServerId(), Equals, uint32(0x0b0e0e0f))
	c.Check(event1.EventLength(), Equals, uint32(len(eventBytes1)))
	c.Check(event1.NextPosition(), Equals, uint32(0xf1f2f3f4))
	c.Check(event1.Flags(), Equals, uint16(0xdead))
	c.Check(event1.Bytes(), DeepEquals, eventBytes1)
	c.Check(
		event1.BasicHeader(),
		DeepEquals,
		eventBytes1[:sizeOfBasicV4EventHeader])
	c.Check(event1.ExtraHeaders(), DeepEquals, []byte{})
	c.Check(event1.FixedLengthData(), DeepEquals, []byte{})
	c.Check(event1.VariableLengthData(), DeepEquals, []byte("\x0a\x0b\x0c"))

	event2, err := s.reader.NextEvent()
	c.Assert(err, IsNil)
	c.Check(event2.SourceName(), Equals, testSourceName)
	c.Check(event2.SourcePosition(), Equals, int64(len(eventBytes1)))
	c.Check(event2.Timestamp(), Equals, uint32(0xdeadbeef))
	c.Check(event2.EventType(), Equals, mysql_proto.LogEventType_Type(0x69))
	c.Check(event2.ServerId(), Equals, uint32(0xdecafbad))
	c.Check(event2.EventLength(), Equals, uint32(len(eventBytes2)))
	c.Check(event2.NextPosition(), Equals, uint32(0x12345678))
	c.Check(event2.Flags(), Equals, uint16(0xf00d))
	c.Check(event2.ExtraHeaders(), DeepEquals, []byte{})
	c.Check(event2.FixedLengthData(), DeepEquals, []byte{})
	c.Check(
		event2.VariableLengthData(),
		DeepEquals,
		[]byte("\xfe\xfe\xfe\xfe\xfe\xfe\xfe"))

	event3, err := s.reader.NextEvent()
	c.Assert(err, IsNil)
	c.Check(event3.SourceName(), Equals, testSourceName)
	c.Check(
		event3.SourcePosition(),
		Equals,
		int64(len(eventBytes1)+len(eventBytes2)))
	c.Check(event3.Timestamp(), Equals, uint32(0x1aaaaaaa))
	c.Check(event3.EventType(), Equals, mysql_proto.LogEventType_Type(0x2b))
	c.Check(event3.ServerId(), Equals, uint32(0x3ccccccc))
	c.Check(event3.EventLength(), Equals, uint32(len(eventBytes3)))
	c.Check(event3.NextPosition(), Equals, uint32(0x4ddddddd))
	c.Check(event3.Flags(), Equals, uint16(0x5eee))
	c.Check(event3.ExtraHeaders(), DeepEquals, []byte{})
	c.Check(event3.FixedLengthData(), DeepEquals, []byte{})
	c.Check(event3.VariableLengthData(), DeepEquals, []byte{})

	_, err = s.src.ReadByte()
	c.Assert(err, Equals, io.EOF)

	event4, err := s.reader.NextEvent()
	c.Check(event4, IsNil)
	c.Check(err, Equals, io.EOF)
}