Beispiel #1
0
// This is used for extracting type / length info from string field's metadata.
func parseTypeAndLength(metadata []byte) (
	fieldType mysql_proto.FieldType_Type,
	length int,
	remaining []byte,
	err error) {

	if len(metadata) < 2 {
		return mysql_proto.FieldType_STRING, 0, nil, errors.New(
			"not enough metadata bytes")
	}

	byte1 := int(metadata[0])
	byte2 := int(metadata[1])

	var realType mysql_proto.FieldType_Type
	if (byte1 & 0x30) != 0x30 { // see mysql issue #37426
		realType = mysql_proto.FieldType_Type(byte1 | 0x30)
		length = byte2 | (((byte1 & 0x30) ^ 0x30) << 4)
	} else {
		realType = mysql_proto.FieldType_Type(byte1)
		length = byte2
	}

	if realType != mysql_proto.FieldType_SET &&
		realType != mysql_proto.FieldType_ENUM &&
		realType != mysql_proto.FieldType_STRING &&
		realType != mysql_proto.FieldType_VAR_STRING {

		return mysql_proto.FieldType_STRING, 0, nil, errors.Newf(
			"Invalid real type: %s (%d)",
			realType.String(),
			realType)
	}

	return realType, length, metadata[2:], nil
}
func (p *TableMapEventParser) parseColumns(t *TableMapEvent) error {
	numCols := len(t.columnTypesBytes)
	t.columnDescriptors = make([]ColumnDescriptor, numCols, numCols)

	metadata := t.metadataBytes

	nullVector, _, err := readBitArray(t.nullColumnsBytes, numCols)
	if err != nil {
		return err
	}

	for idx, colTypeByte := range t.columnTypesBytes {
		colType := mysql_proto.FieldType_Type(colTypeByte)
		realType := colType
		metaLength := 0
		if colType == mysql_proto.FieldType_STRING ||
			colType == mysql_proto.FieldType_VAR_STRING {

			realType, metaLength, metadata, err = parseTypeAndLength(metadata)
			if err != nil {
				return err
			}

			// mysql_proto.FieldType_VAR_STRING is not type polymorphic.
			if colType == mysql_proto.FieldType_VAR_STRING &&
				colType != realType {

				return errors.Newf("Invalid real type: %s (%d)",
					realType.String(),
					realType)
			}
		}

		var fd FieldDescriptor

		nullable := NullableColumn(nullVector[idx])

		switch realType {
		case mysql_proto.FieldType_DECIMAL:
			fd = NewDecimalFieldDescriptor(nullable)
		case mysql_proto.FieldType_TINY:
			fd = NewTinyFieldDescriptor(nullable)
		case mysql_proto.FieldType_SHORT:
			fd = NewShortFieldDescriptor(nullable)
		case mysql_proto.FieldType_LONG:
			fd = NewLongFieldDescriptor(nullable)
		case mysql_proto.FieldType_FLOAT:
			fd, metadata, err = NewFloatFieldDescriptor(nullable, metadata)
		case mysql_proto.FieldType_DOUBLE:
			fd, metadata, err = NewDoubleFieldDescriptor(nullable, metadata)
		case mysql_proto.FieldType_NULL:
			fd = NewNullFieldDescriptor(nullable)
		case mysql_proto.FieldType_TIMESTAMP:
			fd = NewTimestampFieldDescriptor(nullable)
		case mysql_proto.FieldType_LONGLONG:
			fd = NewLongLongFieldDescriptor(nullable)
		case mysql_proto.FieldType_INT24:
			fd = NewInt24FieldDescriptor(nullable)
		case mysql_proto.FieldType_DATE:
			return errors.New("TODO")
		case mysql_proto.FieldType_TIME:
			return errors.New("TODO")
		case mysql_proto.FieldType_DATETIME:
			fd = NewDateTimeFieldDescriptor(nullable)
		case mysql_proto.FieldType_YEAR:
			fd = NewYearFieldDescriptor(nullable)
		case mysql_proto.FieldType_NEWDATE:
			return errors.New("TODO")
		case mysql_proto.FieldType_VARCHAR:
			fd, metadata, err = NewVarcharFieldDescriptor(nullable, metadata)
		case mysql_proto.FieldType_BIT:
			fd, metadata, err = NewBitFieldDescriptor(nullable, metadata)
		case mysql_proto.FieldType_TIMESTAMP2:
			fd, metadata, err = NewTimestamp2FieldDescriptor(nullable, metadata)
		case mysql_proto.FieldType_DATETIME2:
			fd, metadata, err = NewDateTime2FieldDescriptor(nullable, metadata)
		case mysql_proto.FieldType_TIME2:
			return errors.New("TODO")
		case mysql_proto.FieldType_NEWDECIMAL:
			fd, metadata, err = NewNewDecimalFieldDescriptor(nullable, metadata)
		case mysql_proto.FieldType_ENUM:
			return errors.New("Enum type should not appear in binlog")
		case mysql_proto.FieldType_SET:
			return errors.New("Set type should not appear in binlog")
		case mysql_proto.FieldType_TINY_BLOB:
			return errors.New("Tiny blog type should not appear in binlog")
		case mysql_proto.FieldType_MEDIUM_BLOB:
			return errors.New("Medium blog type should not appear in binlog")
		case mysql_proto.FieldType_LONG_BLOB:
			return errors.New("Long blog type should not appear in binlog")
		case mysql_proto.FieldType_BLOB:
			fd, metadata, err = NewBlobFieldDescriptor(nullable, metadata)
		case mysql_proto.FieldType_VAR_STRING, mysql_proto.FieldType_STRING:
			fd = NewStringFieldDescriptor(realType, nullable, metaLength)
		case mysql_proto.FieldType_GEOMETRY:
			return errors.New("TODO")
		default:
			return errors.Newf("Unknown field type: %d", int(realType))
		}

		if err != nil {
			return err
		}

		t.columnDescriptors[idx] = NewColumnDescriptor(fd, idx)
	}

	if len(metadata) != 0 {
		// sanity check
		return errors.New("Not all column metadata is consumed")
	}

	return nil
}