ChDriver. Protocol. Block. Compressed
(ch_driver v0.1.0)
Copy Markdown
Encodes and decodes ClickHouse native-protocol compressed block envelopes.
Wire format (see ClickHouse's CompressionInfo.h / CompressedReadBufferBase.cpp):
[16 bytes] CityHash128 checksum, over everything below
[1 byte] compression method marker (0x02 = NONE, 0x82 = LZ4, 0x90 = ZSTD)
[4 bytes] compressed_size, little-endian -- covers the 9-byte
method+sizes header itself, plus the (possibly compressed) payload
[4 bytes] uncompressed_size, little-endian
[...] payload (compressed unless method is NONE)Multiple blocks are simply concatenated back-to-back with no additional
framing -- callers should call decode/1 repeatedly, threading the
returned rest through, until the buffer is exhausted.
Built on ChDriver.Codec's Rust-NIF-backed LZ4/CityHash primitives --
lz4_compress/1/lz4_decompress/2 for the raw (headerless) LZ4 block
format ClickHouse uses, and cityhash128/1 for the checksum, computed
with the exact CityHash v1.0.2 constants ClickHouse itself uses (not
today's CityHash).
Wired into ChDriver as opt-in compression
Both encode/2 and decode/1 are called from ch_driver, gated behind
its :compression option (:none by default, :lz4 to opt in):
ChDriver.Protocol.Messages.encode_query/2 negotiates compression with the
server via the Query packet's compression flag, encode_empty_data_packet/1
routes the outbound external-table block through encode/2 when enabled,
and ChDriver.Protocol.decode_packet/2 ->
ChDriver.Protocol.NativeBlock.decode_data_packet/2 routes inbound
Data/ProfileEvents blocks through decode/1 the same way. See
ChDriver.Connection's :compression option docs for the client-facing
API and ChDriver.Protocol.Messages.encode_query/2's moduledoc for the
wire-level negotiation details.
Summary
Functions
Decodes a single block envelope from the front of binary.
Encodes iodata into a single compressed block envelope using method
(defaults to :lz4).
Types
Functions
@spec decode(binary()) :: {:ok, binary(), binary()} | {:incomplete, non_neg_integer()} | {:error, term()}
Decodes a single block envelope from the front of binary.
Returns:
{:ok, decompressed, rest}on success, whererestis any unconsumed bytes following this block (e.g. the start of the next one).{:incomplete, missing_byte_count}if not enough bytes are buffered yet to make progress.missing_byte_countis how many more bytes are needed before callingdecode/1again is worth doing.{:error, reason}if the checksum doesn't match or the method byte is unrecognized.
Encodes iodata into a single compressed block envelope using method
(defaults to :lz4).