ChDriver.Protocol (ch_driver v0.1.1)

Copy Markdown

Encodes and decodes ClickHouse native-protocol packets.

This is internal to the driver — application code talks to ClickHouse through ChDriver, not this module directly.

Covers the connection handshake (client_hello/3, encode_client_hello/1, decode_server_hello/1), building outgoing Query/Ping/Cancel packets, and dispatching incoming server packets to the right decoder (decode_packet/2). Per-message byte layouts live in ChDriver.Protocol.Messages; this module owns packet-type dispatch and the ClientHello/ServerHello struct definitions.

Summary

Functions

Whether the handshake needs to send an Addendum packet after ServerHello. Always true for this driver's advertised protocol revision.

Builds a ClientHello struct advertising this driver's identity, for the given default_database, user, and password.

Decodes a single server response packet from the front of binary.

Decodes a ServerHello packet from the front of binary, including its leading packet-type varint (expected to be 0).

Encodes the Addendum packet sent right after receiving ServerHello, when addendum_required?/0 is true.

Encodes a Cancel packet, telling the server to stop running the query currently in progress on this connection. The caller must keep reading until :end_of_stream afterward — see ChDriver.Connection.cancel_stream/2.

Encodes a ClientHello struct into the plaintext bytes sent to open a connection: packet-type varint (0), then client name/version/revision and the default_database/user/password length-prefixed strings.

Encodes the empty Data packet ClickHouse requires right after every Query packet, even for a plain SELECT. compression must match whatever was passed as encode_query/2's :compression for this query.

Encodes a Ping packet. The server replies with a bare Pong and nothing else.

Encodes a Query packet for query_string.

Functions

addendum_required?()

@spec addendum_required?() :: boolean()

Whether the handshake needs to send an Addendum packet after ServerHello. Always true for this driver's advertised protocol revision.

client_hello(default_database \\ "default", user \\ "default", password \\ "")

@spec client_hello(binary(), binary(), binary()) :: ChDriver.Protocol.ClientHello.t()

Builds a ClientHello struct advertising this driver's identity, for the given default_database, user, and password.

decode_packet(binary, compression \\ :none)

@spec decode_packet(binary(), ChDriver.Protocol.Block.Compressed.method()) ::
  {:ok, term(), binary()} | {:incomplete, binary()} | {:error, term()}

Decodes a single server response packet from the front of binary.

Returns {:ok, packet, rest}, {:incomplete, binary} if more bytes are needed (buffer more and retry), or {:error, reason}.

packet is one of:

  • {:data, %{table_name:, columns:, rows:}} -- a query result block.
  • {:profile_events, %{table_name:, columns:, rows:}} -- internal query execution metrics, wire-identical to :data.
  • {:progress, %{read_rows:, read_bytes:, total_rows_to_read:, total_bytes_to_read:, written_rows:, written_bytes:, elapsed_ns:}}
  • :pong
  • :end_of_stream
  • {:exception, %ChDriver.Error{}}

compression (:none (default) or :lz4) must match whatever was negotiated for this query via encode_query/2's :compression opt. It only affects how Data packets' block bodies are decoded; ProfileEvents and every other packet type are always plain regardless of compression.

decode_server_hello(binary)

@spec decode_server_hello(binary()) ::
  {:ok, ChDriver.Protocol.ServerHello.t(), binary()}
  | {:incomplete, binary()}
  | {:error, term()}

Decodes a ServerHello packet from the front of binary, including its leading packet-type varint (expected to be 0).

Returns {:ok, %ServerHello{}, rest}, {:incomplete, binary} if more bytes are needed, or {:error, reason} if the packet type doesn't match Hello (e.g. the server sent an Exception instead, most likely due to bad credentials).

encode_addendum()

@spec encode_addendum() :: iodata()

Encodes the Addendum packet sent right after receiving ServerHello, when addendum_required?/0 is true.

encode_cancel()

@spec encode_cancel() :: iodata()

Encodes a Cancel packet, telling the server to stop running the query currently in progress on this connection. The caller must keep reading until :end_of_stream afterward — see ChDriver.Connection.cancel_stream/2.

encode_client_hello(hello)

@spec encode_client_hello(ChDriver.Protocol.ClientHello.t()) :: iodata()

Encodes a ClientHello struct into the plaintext bytes sent to open a connection: packet-type varint (0), then client name/version/revision and the default_database/user/password length-prefixed strings.

encode_empty_data_packet(compression \\ :none)

@spec encode_empty_data_packet(ChDriver.Protocol.Block.Compressed.method()) ::
  iodata()

Encodes the empty Data packet ClickHouse requires right after every Query packet, even for a plain SELECT. compression must match whatever was passed as encode_query/2's :compression for this query.

encode_ping()

@spec encode_ping() :: iodata()

Encodes a Ping packet. The server replies with a bare Pong and nothing else.

encode_query(query_string, opts \\ [])

@spec encode_query(
  binary(),
  keyword()
) :: iodata()

Encodes a Query packet for query_string.

opts accepts:

  • :query_id -- defaults to "".
  • :params -- a list of {name, raw_text} or {name, raw_text, rounds} tuples binding query_string's {name:Type} placeholders. Build these from Elixir values with ChDriver.Params.text/1 and ChDriver.Params.escape_rounds/1. Defaults to [].
  • :compression -- :none (default) or :lz4.