Pure MoonBit MySQL binlog decoding library
///|
test "validate a file signature and decode a common header" {
@moonbinlog.validate_file_header([0xfe, 0x62, 0x69, 0x6e])
let header : Bytes = [
0x78, 0x56, 0x34, 0x12, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
0x17, 0x00, 0x00, 0x00, 0x00, 0x00,
]
let decoded = @moonbinlog.EventHeader::parse(header, file_offset=4)
assert_eq(decoded.event_size, 19U)
assert_eq(decoded.next_position, 23U)
}moon check --warn-list +unnecessary_annotation --deny-warn
moon test --target native
moon build --target native
moon fmt --check
moon info
python scripts/generate_format_fixtures.py --check
python scripts/generate_table_map_fixtures.py --check
python scripts/generate_integer_fixtures.py --check
python scripts/generate_string_fixtures.py --check
python scripts/generate_blob_fixtures.py --check
python scripts/generate_decimal_fixtures.py --check
python scripts/generate_temporal_fixtures.py --check
python scripts/generate_floating_fixtures.py --check
python scripts/generate_bit_fixtures.py --check
python scripts/generate_enum_set_fixtures.py --check
python scripts/generate_row_image_fixtures.py --check
python scripts/generate_rows_event_fixtures.py --check
python scripts/generate_transaction_fixtures.py --check
python scripts/generate_rotate_fixtures.py --check
python scripts/generate_json_fixtures.py --check
python scripts/verify_json_fixtures.py --check
python scripts/verify_mysql_fixtures.py --check
python scripts/test_cli.py
python scripts/test_rows_cli.py
python scripts/test_transactions_cli.py
python scripts/test_filters_cli.py
python scripts/test_json_cli.py
python scripts/test_schema_template_cli.py
python scripts/test_template_metadata_cli.py
python scripts/test_rotate_cli.py
python scripts/test_chain_cli.py
python scripts/test_previous_gtids_cli.py
python scripts/test_gtid_cli.py
python scripts/test_gtid_transactions_cli.py
python scripts/test_assigned_gtid_cli.py
python scripts/test_stream_cli.py
python scripts/test_stream_rows_cli.py
python scripts/test_stream_transactions_cli.py
python scripts/demo_acceptance.py
python scripts/verify_assigned_gtid_fixtures.py --check
python scripts/generate_previous_gtids_fixtures.py --check
python scripts/generate_gtid_fixtures.py --check
python scripts/generate_schema_example.py --checkpub(all) suberror DecodeError {
InvalidMagic(actual~ : Bytes)
TruncatedInput(file_offset~ : UInt64, expected~ : Int, actual~ : Int)
InvalidEventLength(file_offset~ : UInt64, event_size~ : UInt)
EventTooLarge(file_offset~ : UInt64, event_size~ : UInt, max_event_size~ : UInt)
InvalidEventLimit(max_event_size~ : UInt)
TruncatedEvent(file_offset~ : UInt64, expected~ : UInt, actual~ : UInt)
UnexpectedEventType(file_offset~ : UInt64, expected~ : Byte, actual~ : Byte)
UnsupportedBinlogVersion(file_offset~ : UInt64, version~ : UInt16)
UnsupportedServerVersion(file_offset~ : UInt64, server_version~ : String)
UnsupportedHeaderLength(file_offset~ : UInt64, length~ : Byte)
InvalidFormatDescription(file_offset~ : UInt64, reason~ : String)
UnsupportedChecksumAlgorithm(file_offset~ : UInt64, algorithm~ : Byte)
ChecksumMismatch(file_offset~ : UInt64, expected~ : UInt, actual~ : UInt)
FormatContextResetRequired(file_offset~ : UInt64)
InvalidRotateEvent(file_offset~ : UInt64, reason~ : String)
UnsupportedGtidEvent(file_offset~ : UInt64, event_type~ : Byte)
InvalidGtidEvent(file_offset~ : UInt64, body_offset~ : Int, reason~ : String)
InvalidPreviousGtidsEvent(file_offset~ : UInt64, body_offset~ : Int, reason~ : String)
InvalidGtidSetLimits(max_sids~ : Int, max_intervals~ : Int)
GtidSetLimitExceeded(file_offset~ : UInt64, reason~ : String)
InvalidTableMap(file_offset~ : UInt64, body_offset~ : Int, reason~ : String)
UnsupportedColumnType(file_offset~ : UInt64, column_index~ : Int, wire_type~ : Byte)
InvalidColumnLimit(max_columns~ : Int)
ColumnLimitExceeded(file_offset~ : UInt64, count~ : UInt64, max_columns~ : Int)
InvalidTableContextLimit(max_tables~ : Int, max_bytes~ : UInt64)
TableContextLimitExceeded(file_offset~ : UInt64, reason~ : String)
UnsupportedIntegerColumn(file_offset~ : UInt64, column_index~ : Int, wire_type~ : Byte)
TruncatedColumnValue(file_offset~ : UInt64, column_index~ : Int, expected~ : Int, actual~ : Int)
UnsupportedStringColumn(file_offset~ : UInt64, column_index~ : Int, wire_type~ : Byte)
InvalidStringMetadata(file_offset~ : UInt64, column_index~ : Int, reason~ : String)
StringLengthExceeded(file_offset~ : UInt64, column_index~ : Int, length~ : Int, max_length~ : Int)
UnsupportedBlobColumn(file_offset~ : UInt64, column_index~ : Int, wire_type~ : Byte)
InvalidBlobMetadata(file_offset~ : UInt64, column_index~ : Int, reason~ : String)
InvalidBlobLimit(max_value_bytes~ : Int)
BlobTooLarge(file_offset~ : UInt64, column_index~ : Int, length~ : UInt64, max_value_bytes~ : Int)
UnsupportedDecimalColumn(file_offset~ : UInt64, column_index~ : Int, wire_type~ : Byte)
InvalidDecimalMetadata(file_offset~ : UInt64, column_index~ : Int, reason~ : String)
InvalidDecimalValue(file_offset~ : UInt64, column_index~ : Int, value_offset~ : Int)
UnsupportedTemporalColumn(file_offset~ : UInt64, column_index~ : Int, wire_type~ : Byte)
InvalidTemporalMetadata(file_offset~ : UInt64, column_index~ : Int, reason~ : String)
InvalidTemporalValue(file_offset~ : UInt64, column_index~ : Int, reason~ : String)
UnsupportedFloatingColumn(file_offset~ : UInt64, column_index~ : Int, wire_type~ : Byte)
InvalidFloatingMetadata(file_offset~ : UInt64, column_index~ : Int, expected_width~ : Int)
NonFiniteFloatingValue(file_offset~ : UInt64, column_index~ : Int, wire_type~ : Byte, bits~ : UInt64)
UnsupportedBitColumn(file_offset~ : UInt64, column_index~ : Int, wire_type~ : Byte)
InvalidBitMetadata(file_offset~ : UInt64, column_index~ : Int, reason~ : String)
UnsupportedEnumSetColumn(file_offset~ : UInt64, column_index~ : Int, wire_type~ : Byte)
InvalidEnumSetMetadata(file_offset~ : UInt64, column_index~ : Int, reason~ : String)
InvalidRowImage(file_offset~ : UInt64, reason~ : String)
InvalidRowImageLimit(max_bytes~ : Int)
RowImageLimitExceeded(file_offset~ : UInt64, max_bytes~ : Int)
UnexpectedRowNull(file_offset~ : UInt64, column_index~ : Int)
MissingIntegerSignedness(file_offset~ : UInt64, column_index~ : Int)
UnsupportedRowColumn(file_offset~ : UInt64, column_index~ : Int, wire_type~ : Byte)
UnsupportedJsonColumn(file_offset~ : UInt64, column_index~ : Int, wire_type~ : Byte)
InvalidJsonMetadata(file_offset~ : UInt64, column_index~ : Int)
InvalidJsonLimits(max_value_bytes~ : Int, max_depth~ : Int, max_nodes~ : Int)
JsonLimitExceeded(file_offset~ : UInt64, column_index~ : Int, reason~ : String)
InvalidJsonValue(file_offset~ : UInt64, column_index~ : Int, value_offset~ : Int, reason~ : String)
UnsupportedJsonType(file_offset~ : UInt64, column_index~ : Int, value_offset~ : Int, binary_type~ : Byte)
UnsupportedRowsEvent(file_offset~ : UInt64, event_type~ : Byte)
InvalidRowsEvent(file_offset~ : UInt64, body_offset~ : Int, reason~ : String)
MissingTableMap(file_offset~ : UInt64, table_id~ : UInt64)
InvalidRowsLimit(max_rows~ : Int)
RowsLimitExceeded(file_offset~ : UInt64, max_rows~ : Int)
UnsupportedTransactionEvent(file_offset~ : UInt64, event_type~ : Byte)
InvalidTransactionEvent(file_offset~ : UInt64, reason~ : String)
UnsupportedTransactionStatement(file_offset~ : UInt64)
InvalidTransactionState(file_offset~ : UInt64, reason~ : String)
InvalidTransactionLimits(max_events~ : Int, max_changes~ : Int, max_bytes~ : UInt64)
TransactionLimitExceeded(file_offset~ : UInt64, reason~ : String)
IncompleteTransaction(start_offset~ : UInt64)
InvalidGtidTransactionState(file_offset~ : UInt64, reason~ : String)
IncompleteGtidTransaction(start_offset~ : UInt64)
GtidTransactionFailed
MissingFormatDescription(file_offset~ : UInt64)
FileReaderFailed(file_offset~ : UInt64)
StreamAlreadyFinished(file_offset~ : UInt64)
InvalidStreamOffset(file_offset~ : UInt64)
} derive(Eq, Debug)pub(all) suberror SchemaError {
InvalidSchema(path~ : String, reason~ : String)
InvalidSchemaLimits(max_tables~ : Int, max_columns~ : Int, max_input_units~ : Int)
SchemaLimitExceeded(path~ : String, reason~ : String)
MissingTableSchema(file_offset~ : UInt64, database~ : String, table~ : String)
SchemaMismatch(file_offset~ : UInt64, database~ : String, table~ : String, column_index~ : Int?, reason~ : String)
} derive(Eq, Debug)pub(all) enum BinaryJsonValue {
Null
Boolean(Bool)
Integer(IntegerValue)
DoubleNumber(value~ : Double, bits~ : UInt64)
Text(String)
Array(ReadOnlyArray[BinaryJsonValue])
Object(ReadOnlyArray[(String, BinaryJsonValue)])
} derive(Eq, Debug)test {
let value = @moonbinlog.BinaryJsonValue::Integer(
Unsigned(18446744073709551615UL),
)
assert_eq(value.to_json_text(), "18446744073709551615")
}pub struct BinlogEventStream {
// private fields
}fn BinlogEventStream::feed(self : BinlogEventStream, data : BytesView) -> BinlogFileEvent raise DecodeErrorfn BinlogEventStream::new(signature : BytesView, max_event_size? : UInt) -> BinlogEventStream raise DecodeErrorpub struct BinlogFileEvent {
header : EventHeader
raw : BytesView
body : BytesView
format : FormatDescription
} derive(Eq, Debug)pub struct BinlogFileReader {
// private fields
}fn BinlogFileReader::new(data : BytesView, max_event_size? : UInt) -> BinlogFileReader raise DecodeErrortest {
// A valid signature initializes the reader; next() still requires FDE.
let reader = @moonbinlog.BinlogFileReader::new([0xfe, 0x62, 0x69, 0x6e])
assert_eq(reader.position(), 4UL)
assert_eq(reader.format(), None)
}pub struct BoundTableSchema {
table_map : TableMap
schema : TableSchema
integer_unsigned : ReadOnlyArray[Bool?]
} derive(Eq, Debug)pub struct DecodedFloating {
value : FloatingValue
bits : UInt64
bytes_read : Int
} derive(Eq, Debug)pub struct DecodedTemporal {
value : TemporalValue
fractional_precision : Int
bytes_read : Int
} derive(Eq, Debug)fn EventHeader::parse(data : BytesView, file_offset~ : UInt64, max_event_size? : UInt) -> EventHeader raise DecodeErrorpub struct FormatDescription {
header : EventHeader
binlog_version : UInt16
server_version : String
created_timestamp : UInt
common_header_length : Byte
post_header_lengths : Bytes
checksum_algorithm : ChecksumAlgorithm
} derive(Eq, Debug)fn FormatDescription::parse(data : BytesView, file_offset~ : UInt64, max_event_size? : UInt) -> FormatDescription raise DecodeErrorfn FormatDescription::payload(self : FormatDescription, data : BytesView, file_offset~ : UInt64, max_event_size? : UInt) -> BytesView raise DecodeErrorpub struct GtidEvent {
header : EventHeader
flags : Byte
sid : Bytes
gno : UInt64
last_committed : UInt64
sequence_number : UInt64
commit_timestamps : GtidCommitTimestamps?
transaction_length : UInt64?
server_versions : GtidServerVersions?
commit_group_ticket : UInt64?
} derive(Eq, Debug)fn GtidEvent::parse(format : FormatDescription, data : BytesView, file_offset~ : UInt64, max_event_size? : UInt) -> GtidEvent raise DecodeErrorpub struct GtidSidIntervals {
sid : Bytes
intervals : ReadOnlyArray[GtidInterval]
} derive(Eq, Debug)pub struct GtidTransactionDecoder {
// private fields
}fn GtidTransactionDecoder::feed(self : GtidTransactionDecoder, data : BytesView, file_offset~ : UInt64, integer_unsigned? : ReadOnlyArray[Bool?]) -> GtidTransactionStep raise DecodeErrorfn GtidTransactionDecoder::new(format : FormatDescription, max_events? : Int, max_changes? : Int, max_bytes? : UInt64) -> GtidTransactionDecoder raise DecodeErrorpub struct GtidTransactionOutcome {
gtid : GtidEvent
transaction : TransactionOutcome
start_offset : UInt64
end_offset : UInt64
event_count : Int
wire_bytes : UInt64
} derive(Eq, Debug)fn GtidTransactionOutcome::not_equal(x : GtidTransactionOutcome, y : GtidTransactionOutcome) -> Boolpub struct GtidTransactionStep {
header : EventHeader
outcome : GtidTransactionOutcome?
} derive(Eq, Debug)pub struct PreviousGtidsEvent {
header : EventHeader
sids : ReadOnlyArray[GtidSidIntervals]
} derive(Eq, Debug)fn PreviousGtidsEvent::parse(format : FormatDescription, data : BytesView, file_offset~ : UInt64, max_event_size? : UInt, max_sids? : Int, max_intervals? : Int) -> PreviousGtidsEvent raise DecodeErrorpub struct RotateEvent {
header : EventHeader
position : UInt64
next_binlog : Bytes
} derive(Eq, Debug)fn RotateEvent::parse(format : FormatDescription, data : BytesView, file_offset~ : UInt64, max_event_size? : UInt) -> RotateEvent raise DecodeErrorpub(all) enum RowCell {
Absent
Null
Integer(IntegerValue)
Floating(DecodedFloating)
StringBytes(Bytes)
BlobBytes(Bytes)
JsonDocument(DecodedJson)
Decimal(DecodedDecimal)
Bit(DecodedBit)
EnumSet(EnumSetValue)
Temporal(DecodedTemporal)
} derive(Eq, Debug)pub struct RowsEvent {
header : EventHeader
table : TableMap
flags : UInt16
extra_data : Bytes
columns_before : Bytes?
columns_after : Bytes?
changes : ReadOnlyArray[RowChange]
} derive(Eq, Debug)pub struct SchemaConfig {
// private fields
}fn SchemaConfig::bind(self : SchemaConfig, table_map : TableMap) -> BoundTableSchema raise SchemaErrorfn SchemaConfig::parse(text : StringView, max_tables? : Int, max_columns? : Int, max_input_units? : Int) -> SchemaConfig raise SchemaErrortest {
let config = @moonbinlog.SchemaConfig::parse(
"{\"schema_version\":1,\"tables\":[]}",
)
assert_eq(config.length(), 0)
assert_eq(config.get("shop", "orders"), None)
}fn TableColumn::decode_bit(self : TableColumn, data : BytesView, file_offset~ : UInt64, column_index~ : Int) -> DecodedBit raise DecodeErrorfn TableColumn::decode_blob(self : TableColumn, data : BytesView, file_offset~ : UInt64, column_index~ : Int, max_value_bytes? : Int) -> DecodedBlob raise DecodeErrorfn TableColumn::decode_decimal(self : TableColumn, data : BytesView, file_offset~ : UInt64, column_index~ : Int) -> DecodedDecimal raise DecodeErrorfn TableColumn::decode_enum_set(self : TableColumn, data : BytesView, file_offset~ : UInt64, column_index~ : Int) -> DecodedEnumSet raise DecodeErrorfn TableColumn::decode_floating(self : TableColumn, data : BytesView, file_offset~ : UInt64, column_index~ : Int) -> DecodedFloating raise DecodeErrorfn TableColumn::decode_integer(self : TableColumn, data : BytesView, unsigned~ : Bool, file_offset~ : UInt64, column_index~ : Int) -> DecodedInteger raise DecodeErrorfn TableColumn::decode_json(self : TableColumn, data : BytesView, file_offset~ : UInt64, column_index~ : Int, max_value_bytes? : Int, max_depth? : Int, max_nodes? : Int) -> DecodedJson raise DecodeErrorfn TableColumn::decode_string(self : TableColumn, data : BytesView, file_offset~ : UInt64, column_index~ : Int) -> DecodedString raise DecodeErrorfn TableColumn::decode_temporal(self : TableColumn, data : BytesView, file_offset~ : UInt64, column_index~ : Int) -> DecodedTemporal raise DecodeErrorpub struct TableMap {
header : EventHeader
table_id : UInt64
flags : UInt16
database : String
table : String
columns : ReadOnlyArray[TableColumn]
optional_metadata : ReadOnlyArray[TableMetadataField]
} derive(Eq, Debug)fn TableMap::decode_row_image(self : TableMap, data : BytesView, columns_present~ : BytesView, integer_unsigned~ : ReadOnlyArray[Bool?], file_offset~ : UInt64, max_bytes? : Int) -> RowImage raise DecodeErrorfn TableMap::parse(format : FormatDescription, data : BytesView, file_offset~ : UInt64, max_columns? : Int, max_event_size? : UInt) -> TableMap raise DecodeErrorpub struct TableMapContext {
// private fields
}fn TableMapContext::apply(self : TableMapContext, data : BytesView, file_offset~ : UInt64, max_columns? : Int, max_event_size? : UInt) -> TableMap raise DecodeErrorfn TableMapContext::decode_rows(self : TableMapContext, data : BytesView, integer_unsigned~ : ReadOnlyArray[Bool?], file_offset~ : UInt64, max_rows? : Int, max_row_bytes? : Int, max_event_size? : UInt) -> RowsEvent raise DecodeErrorfn TableMapContext::new(format : FormatDescription, max_tables? : Int, max_bytes? : UInt64) -> TableMapContext raise DecodeErrorpub struct TableSchema {
database : String
table : String
columns : ReadOnlyArray[SchemaColumn]
} derive(Eq, Debug)pub(all) enum TemporalValue {
Year(Int)
Date(year~ : Int, month~ : Int, day~ : Int)
DateTime(year~ : Int, month~ : Int, day~ : Int, hour~ : Int, minute~ : Int, second~ : Int, microsecond~ : Int)
Time(negative~ : Bool, hour~ : Int, minute~ : Int, second~ : Int, microsecond~ : Int)
Timestamp(seconds~ : UInt, microsecond~ : Int)
} derive(Eq, Debug)fn TransactionBoundary::parse(format : FormatDescription, data : BytesView, file_offset~ : UInt64) -> TransactionBoundary raise DecodeErrorpub struct TransactionDecoder {
// private fields
}fn TransactionDecoder::feed(self : TransactionDecoder, data : BytesView, file_offset~ : UInt64, integer_unsigned? : ReadOnlyArray[Bool?]) -> TransactionStep raise DecodeErrorfn TransactionDecoder::new(format : FormatDescription, max_events? : Int, max_changes? : Int, max_bytes? : UInt64) -> TransactionDecoder raise DecodeErrorpub(all) enum TransactionOutcome {
Committed(CommittedTransaction)
RolledBack(start_offset~ : UInt64, end_offset~ : UInt64, discarded_changes~ : Int)
} derive(Eq, Debug)test {
@moonbinlog.validate_file_header([0xfe, 0x62, 0x69, 0x6e])
}