The official Rust client for connecting to ClickHouse, originally developed by Paul Loyd. The client source code is available in the GitHub repository.Documentation Index
Fetch the complete documentation index at: https://private-7c7dfe99-page-updates.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
Overview
- Uses
serdefor encoding/decoding rows. - Supports
serdeattributes:skip_serializing,skip_deserializing,rename. - Uses
RowBinaryformat over the HTTP transport.- There are plans to switch to
Nativeover TCP.
- There are plans to switch to
- Supports TLS (via
native-tlsandrustls-tlsfeatures). - Supports compression and decompression (LZ4).
- Provides APIs for selecting or inserting data, executing DDLs, and client-side batching.
- Provides convenient mocks for unit testing.
Installation
To use the crate, add the following to yourCargo.toml:
Cargo features
lz4(enabled by default) — enablesCompression::Lz4andCompression::Lz4Hc(_)variants. If enabled,Compression::Lz4is used by default for all queries except forWATCH.native-tls— supports urls with theHTTPSschema viahyper-tls, which links against OpenSSL.rustls-tls— supports urls with theHTTPSschema viahyper-rustls, which doesn’t link against OpenSSL.inserter— enablesclient.inserter().test-util— adds mocks. See the example. Use it only indev-dependencies.watch— enablesclient.watchfunctionality. See the corresponding section for details.uuid— addsserde::uuidto work with uuid crate.time— addsserde::timeto work with time crate.
HTTPS url, either the native-tls or rustls-tls feature should be enabled.
If both are enabled, the rustls-tls feature will take precedence.
:::
ClickHouse versions compatibility
The client is compatible with the LTS or newer versions of ClickHouse, as well as ClickHouse Cloud. ClickHouse server older than v22.6 handles RowBinary incorrectly in some rare cases. You could use v0.11+ and enablewa-37420 feature to solve this problem. Note: this feature shouldn’t be used with newer ClickHouse versions.
Examples
We aim to cover various scenarios of client usage with the examples in the client repository. The overview is available in the examples README. If something is unclear or missing from the examples or from the following documentation, feel free to contact us.Usage
ch2rs crate is useful to generate a row type from ClickHouse.
Creating a client instance
HTTPS or ClickHouse Cloud connection
HTTPS works with eitherrustls-tls or native-tls cargo features.
Then, create client as usual. In this example, the environment variables are used to store the connection details:
:::important
The URL should include both protocol and port, e.g. https://instance.clickhouse.cloud:8443.
:::
- HTTPS with ClickHouse Cloud example in the client repo. This should be applicable to on-premise HTTPS connections as well.
Selecting rows
- Placeholder
?fieldsis replaced withno, name(fields ofRow). - Placeholder
?is replaced with values in followingbind()calls. - Convenient
fetch_one::<Row>()andfetch_all::<Row>()methods can be used to get a first row or all rows, correspondingly. sql::Identifiercan be used to bind table names.
query(...).with_option("wait_end_of_query", "1") in order to enable response buffering on the server-side. More details. The buffer_size option can be useful, too.
Inserting rows
- If
end()isn’t called, theINSERTis aborted. - Rows are being sent progressively as a stream to spread the network load.
- ClickHouse inserts batches atomically only if all rows fit in the same partition and their number is less
max_insert_block_size.
Async insert (server-side batching)
You could use ClickHouse asynchronous inserts to avoid client-side batching of the incoming data. This can be done by simply providing theasync_insert option to the insert method (or even to the Client instance itself, so that it will affect all the insert calls).
- Async insert example in the client repo.
Inserter feature (client-side batching)
Requires theinserter cargo feature.
Inserterends the active insert incommit()if any of the thresholds (max_bytes,max_rows,period) are reached.- The interval between ending active
INSERTs can be biased by usingwith_period_biasto avoid load spikes by parallel inserters. Inserter::time_left()can be used to detect when the current period ends. CallInserter::commit()again to check limits if your stream emits items rarely.- Time thresholds implemented by using quanta crate to speed the
inserterup. Not used iftest-utilis enabled (thus, time can be managed bytokio::time::advance()in custom tests). - All rows between
commit()calls are inserted in the sameINSERTstatement.
Executing DDLs
With a single-node deployment, it is enough to execute DDLs like this:wait_end_of_query option. This can be done like this:
ClickHouse settings
You can apply various ClickHouse settings using thewith_option method. For example:
query, it works similarly with insert and inserter methods; additionally, the same method can be called on the Client instance to set global settings for all queries.
Query ID
Using.with_option, you can set the query_id option to identify queries in the ClickHouse query log.
query, it works similarly with insert and inserter methods.
See also: query_id example in the client repo.
Session ID
Similarly toquery_id, you can set the session_id to execute the statements in the same session. session_id can be set either globally on the client level, or per query, insert, or inserter call.
Custom HTTP headers
If you’re using proxy authentication or need to pass custom headers, you can do it like this:Custom HTTP client
This could be useful for tweaking the underlying HTTP connection pool settings.Data types
See also the additional examples:
(U)Int(8|16|32|64|128)maps to/from corresponding(u|i)(8|16|32|64|128)types or newtypes around them.(U)Int256aren’t supported directly, but there is a workaround for it.Float(32|64)maps to/from correspondingf(32|64)or newtypes around them.Decimal(32|64|128)maps to/from correspondingi(32|64|128)or newtypes around them. It’s more convenient to usefixnumor another implementation of signed fixed-point numbers.Booleanmaps to/fromboolor newtypes around it.Stringmaps to/from any string or bytes types, e.g.&str,&[u8],String,Vec<u8>orSmartString. New types are also supported. To store bytes, consider usingserde_bytes, because it’s more efficient.
FixedString(N)is supported as an array of bytes, e.g.[u8; N].
Enum(8|16)are supported usingserde_repr.
UUIDmaps to/fromuuid::Uuidby usingserde::uuid. Requires theuuidfeature.
IPv6maps to/fromstd::net::Ipv6Addr.IPv4maps to/fromstd::net::Ipv4Addrby usingserde::ipv4.
Datemaps to/fromu16or a newtype around it and represents a number of days elapsed since1970-01-01. Also,time::Dateis supported by usingserde::time::date, that requires thetimefeature.
Date32maps to/fromi32or a newtype around it and represents a number of days elapsed since1970-01-01. Also,time::Dateis supported by usingserde::time::date32, that requires thetimefeature.
DateTimemaps to/fromu32or a newtype around it and represents a number of seconds elapsed since UNIX epoch. Also,time::OffsetDateTimeis supported by usingserde::time::datetime, that requires thetimefeature.
DateTime64(_)maps to/fromi32or a newtype around it and represents a time elapsed since UNIX epoch. Also,time::OffsetDateTimeis supported by usingserde::time::datetime64::*, that requires thetimefeature.
Tuple(A, B, ...)maps to/from(A, B, ...)or a newtype around it.Array(_)maps to/from any slice, e.g.Vec<_>,&[_]. New types are also supported.Map(K, V)behaves likeArray((K, V)).LowCardinality(_)is supported seamlessly.Nullable(_)maps to/fromOption<_>. Forclickhouse::serde::*helpers add::option.
Nestedis supported by providing multiple arrays with renaming.
Geotypes are supported.Pointbehaves like a tuple(f64, f64), and the rest of the types are just slices of points.
Variant,Dynamic, (new)JSONdata types aren’t supported yet.
Mocking
The crate provides utils for mocking CH server and testing DDL,SELECT, INSERT and WATCH queries. The functionality can be enabled with the test-util feature. Use it only as a dev-dependency.
See the example.
Troubleshooting
CANNOT_READ_ALL_DATA
The most common cause for theCANNOT_READ_ALL_DATA error is that the row definition on the application side does match that in ClickHouse.
Consider the following table:
EventLog is defined on the application side with mismatching types, e.g.:
EventLog struct:
Known limitations
Variant,Dynamic, (new)JSONdata types aren’t supported yet.- Server-side parameter binding isn’t supported yet; see this issue for tracking.
