ChDriver (ch_driver v0.1.0)
Copy MarkdownPublic API for the ClickHouse native-protocol driver: a DBConnection
pool wrapping ChDriver.Connection's TCP handshake and query encoding.
Usage
{:ok, pool} = ChDriver.start_link(hostname: "localhost", port: 9000)
{:ok, %ChDriver.Result{columns: columns, rows: rows}} =
ChDriver.query(pool, "SELECT number FROM system.numbers LIMIT 5")This is the intended integration point for higher layers (e.g.
Ecto.Adapters.ClickHouse) -- start_link/1, query/2,3,4,
query!/2,3,4, and stream/2,3,4 are the whole public surface;
everything else (ChDriver.DBConnection, ChDriver.Connection,
ChDriver.Protocol) is wiring underneath it.
All ChDriver.Connection.connect/1 options (:hostname, :port,
:database, :username, :password, :connect_timeout,
:recv_timeout, :compression) are accepted by start_link/1 and
forwarded to each pooled connection; standard DBConnection.start_link/2
pool options (:pool_size, :name, etc.) are also accepted.
:compression (:none (default) or :lz4) is opt-in wire compression
for Data blocks -- off by default, so existing callers see unchanged
behavior. It can also be overridden per call via query/4's opts. See
ChDriver.Connection.connect/1 and
ChDriver.Protocol.Messages.encode_query/2 for how it's negotiated with
the server.
Summary
Functions
Runs statement (a raw SQL string, optionally containing ClickHouse
{name:Type} parameter placeholders) against conn (a pool started by
start_link/1, or any DBConnection-compatible connection reference)
and returns {:ok, %ChDriver.Result{}} or {:error, reason}.
Same as query/4, but returns the %ChDriver.Result{} directly on success
and raises the underlying error instead of returning {:error, reason}.
Starts a DBConnection pool of ClickHouse native-protocol connections.
Builds a %ChDriver.Stream{} that lazily executes statement and
yields its rows one wire-protocol Data block at a time (each element is
a %ChDriver.Result{} for that block, honoring opts[:decode_mapper]
exactly like query/2,3,4), instead of buffering the whole result in
memory the way query/2,3,4 does.
Functions
@spec query(DBConnection.conn(), binary(), list(), keyword()) :: {:ok, ChDriver.Result.t()} | {:error, Exception.t()}
Runs statement (a raw SQL string, optionally containing ClickHouse
{name:Type} parameter placeholders) against conn (a pool started by
start_link/1, or any DBConnection-compatible connection reference)
and returns {:ok, %ChDriver.Result{}} or {:error, reason}.
params is a list of {name, raw_text} or {name, raw_text, escape_rounds}
tuples binding statement's placeholders -- see
ChDriver.Params.text/1/ChDriver.Params.escape_rounds/1 and
ChDriver.Query's moduledoc.
@spec query!(DBConnection.conn(), binary(), list(), keyword()) :: ChDriver.Result.t()
Same as query/4, but returns the %ChDriver.Result{} directly on success
and raises the underlying error instead of returning {:error, reason}.
Starts a DBConnection pool of ClickHouse native-protocol connections.
Returns {:ok, pid} (the pool/pid to pass as conn to query/2,3) or
{:error, reason}.
@spec stream(DBConnection.conn(), binary(), list(), keyword()) :: ChDriver.Stream.t()
Builds a %ChDriver.Stream{} that lazily executes statement and
yields its rows one wire-protocol Data block at a time (each element is
a %ChDriver.Result{} for that block, honoring opts[:decode_mapper]
exactly like query/2,3,4), instead of buffering the whole result in
memory the way query/2,3,4 does.
conn must already be a checked-out %DBConnection{} -- exactly what
DBConnection.stream/4 itself requires (its resource/5 helper only
matches an already-checked-out struct) -- so stream/2,3,4 has to be
called from inside DBConnection.run/3 or DBConnection.transaction/3,
not directly against a pool pid:
{:ok, result} =
DBConnection.run(pool, fn conn ->
conn
|> ChDriver.stream("SELECT number FROM system.numbers LIMIT 200000")
|> Enum.take(5)
end)See ChDriver.Stream's moduledoc and ChDriver.DBConnection's cursor
section for how the underlying handle_declare/4/handle_fetch/4/
handle_deallocate/4 cycle keeps this genuinely incremental (each
block is only read off the socket when the consumer asks for it) rather
than pre-buffering everything and merely presenting it lazily.