> ## Documentation Index
> Fetch the complete documentation index at: https://clickhouse.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

> 用于连接 ClickHouse 的官方 Rust 客户端。

# ClickHouse Rust 客户端

这是 ClickHouse 的官方 Rust 客户端，最初由 [Paul Loyd](https://github.com/loyd) 开发。客户端源代码可在 [GitHub 仓库](https://github.com/ClickHouse/clickhouse-rs) 中获取。

<div id="overview">
  ## 概述
</div>

* 使用 `serde` 对行进行编码和解码。
* 支持 `serde` 属性：`skip_serializing`、`skip_deserializing`、`rename`。
* 通过 HTTP 传输使用 [`RowBinary`](/docs/zh/reference/formats/RowBinary/RowBinary) 格式。
  * 计划切换为通过 TCP 使用 [`Native`](/docs/zh/reference/formats/Native) 格式。
* 支持 TLS (通过 `native-tls` 和 `rustls-tls` 功能特性) 。
* 支持压缩和解压缩 (LZ4) 。
* 提供用于查询或插入数据、执行 DDL 语句以及进行客户端批处理的 API。
* 为单元测试提供便捷的 mock。

<div id="installation">
  ## 安装
</div>

要使用该 crate，请将以下内容添加到 `Cargo.toml`：

```toml theme={null}
[dependencies]
clickhouse = "0.12.2"

[dev-dependencies]
clickhouse = { version = "0.12.2", features = ["test-util"] }
```

另请参阅：[crates.io 页面](https://crates.io/crates/clickhouse).

<div id="cargo-features">
  ## Cargo 特性
</div>

* `lz4` (默认启用) — 启用 `Compression::Lz4` 和 `Compression::Lz4Hc(_)` Variant。启用后，除 `WATCH` 外，所有查询默认都使用 `Compression::Lz4`。
* `native-tls` — 通过 `hyper-tls` 支持 `HTTPS` schema 的 URL，并链接 OpenSSL。
* `rustls-tls` — 通过 `hyper-rustls` 支持 `HTTPS` schema 的 URL，且不链接 OpenSSL。
* `inserter` — 启用 `client.inserter()`。
* `test-util` — 添加 mock。参见[示例](https://github.com/ClickHouse/clickhouse-rs/tree/main/examples/mock.rs)。仅在 `dev-dependencies` 中使用。
* `watch` — 启用 `client.watch` 功能。详见对应章节。
* `uuid` — 添加 `serde::uuid`，以便配合 [uuid](https://docs.rs/uuid) crate 使用。
* `time` — 添加 `serde::time`，以便配合 [time](https://docs.rs/time) crate 使用。

<Warning>
  通过 `HTTPS` URL 连接到 ClickHouse 时，应启用 `native-tls` 或 `rustls-tls` 两项特性中的一项。
  如果两者都启用，则 `rustls-tls` 特性具有更高优先次序。
</Warning>

<div id="clickhouse-versions-compatibility">
  ## ClickHouse 版本兼容性
</div>

该客户端兼容 ClickHouse 长期支持版及更高版本，也兼容 ClickHouse Cloud。

早于 v22.6 的 ClickHouse server 在某些极少数情况下会[错误处理](https://github.com/ClickHouse/ClickHouse/issues/37420) RowBinary。
你可以使用 v0.11+ 并启用 `wa-37420` 功能来解决此问题。注意：不要将此功能用于较新的 ClickHouse 版本。

<div id="examples">
  ## 示例
</div>

我们希望通过客户端代码仓库中的 [示例](https://github.com/ClickHouse/clickhouse-rs/blob/main/examples) 覆盖客户端使用的各种场景。概览可参见 [示例 README](https://github.com/ClickHouse/clickhouse-rs/blob/main/examples/README.md#overview)。

如果示例或下文档中有任何不清楚或缺失的内容，欢迎随时[联系我们](/docs/zh/integrations/language-clients/rust/index#contact-us)。

<div id="usage">
  ## 用法
</div>

<Note>
  [ch2rs](https://github.com/ClickHouse/ch2rs) crate 可用于根据 ClickHouse 生成行类型。
</Note>

<div id="creating-a-client-instance">
  ### 创建客户端实例
</div>

<Tip>
  复用已创建的客户端，或对其进行克隆，从而复用底层的 hyper 连接池。
</Tip>

```rust theme={null}
use clickhouse::Client;

let client = Client::default()
    // should include both protocol and port
    .with_url("http://localhost:8123")
    .with_user("name")
    .with_password("123")
    .with_database("test");
```

<div id="https-or-clickhouse-cloud-connection">
  ### HTTPS 或 ClickHouse Cloud 连接
</div>

HTTPS 可与 `rustls-tls` 或 `native-tls` cargo feature 搭配使用。

然后，像平常一样创建 client。在此示例中，使用环境变量存储连接信息：

<Warning>
  URL 应同时包含协议和端口，例如 `https://instance.clickhouse.cloud:8443`。
</Warning>

```rust theme={null}
fn read_env_var(key: &str) -> String {
    env::var(key).unwrap_or_else(|_| panic!("{key} env variable should be set"))
}

let client = Client::default()
    .with_url(read_env_var("CLICKHOUSE_URL"))
    .with_user(read_env_var("CLICKHOUSE_USER"))
    .with_password(read_env_var("CLICKHOUSE_PASSWORD"));
```

另请参阅：

* 客户端仓库中的 [ClickHouse Cloud HTTPS 示例](https://github.com/ClickHouse/clickhouse-rs/blob/main/examples/clickhouse_cloud.rs)。这也同样适用于本地部署环境中的 HTTPS 连接。

<div id="selecting-rows">
  ### 查询行
</div>

```rust theme={null}
use serde::Deserialize;
use clickhouse::Row;
use clickhouse::sql::Identifier;

#[derive(Row, Deserialize)]
struct MyRow<'a> {
    no: u32,
    name: &'a str,
}

let table_name = "some";
let mut cursor = client
    .query("SELECT ?fields FROM ? WHERE no BETWEEN ? AND ?")
    .bind(Identifier(table_name))
    .bind(500)
    .bind(504)
    .fetch::<MyRow<'_>>()?;

while let Some(row) = cursor.next().await? { .. }
```

* 占位符 `?fields` 会被替换为 `no, name` (`Row` 的字段) 。
* 占位符 `?` 会被替换为后续 `bind()` 调用中传入的值。
* 可以使用便捷的 `fetch_one::<Row>()` 和 `fetch_all::<Row>()` 方法，分别获取第一行或全部行。
* 可以使用 `sql::Identifier` 来绑定表名。

注意：由于整个响应是以流式方式返回的，游标即使在已经返回了一些行之后，也仍然可能报错。如果你的用例中遇到这种情况，可以尝试使用 `query(...).with_option("wait_end_of_query", "1")` 在服务端启用响应缓冲。[更多详情](/docs/zh/concepts/features/interfaces/http#response-buffering)。`buffer_size` 选项也可能有帮助。

<Warning>
  在查询行时请谨慎使用 `wait_end_of_query`，因为它会增加服务端的内存消耗，并且很可能降低整体性能。
</Warning>

<div id="inserting-rows">
  ### 插入数据行
</div>

```rust theme={null}
use serde::Serialize;
use clickhouse::Row;

#[derive(Row, Serialize)]
struct MyRow {
    no: u32,
    name: String,
}

let mut insert = client.insert("some")?;
insert.write(&MyRow { no: 0, name: "foo".into() }).await?;
insert.write(&MyRow { no: 1, name: "bar".into() }).await?;
insert.end().await?;
```

* 如果未调用 `end()`，`INSERT` 会被中止。
* 行会以 stream 的形式逐步发送，以分散网络负载。
* 只有当所有行都位于同一分区，且行数小于 [`max_insert_block_size`](/docs/zh/reference/settings/session-settings#max_insert_block_size) 时，ClickHouse 才会以原子方式插入批次。

<div id="async-insert-server-side-batching">
  ### 异步插入 (服务端批处理)
</div>

你可以使用 [ClickHouse 异步插入](/docs/zh/concepts/features/operations/insert/asyncinserts) 来避免在客户端对传入数据进行批处理。只需将 `async_insert` 选项传给 `insert` 方法 (甚至可以直接设置在 `Client` 实例上，这样会影响所有 `insert` 调用) 即可。

```rust theme={null}
let client = Client::default()
    .with_url("http://localhost:8123")
    .with_option("async_insert", "1")
    .with_option("wait_for_async_insert", "0");
```

另请参阅：

* [async insert 示例](https://github.com/ClickHouse/clickhouse-rs/blob/main/examples/async_insert.rs) (位于客户端仓库中) 。

<div id="inserter-feature-client-side-batching">
  ### Inserter 功能 (客户端批处理)
</div>

需要启用 `inserter` Cargo 功能。

```rust theme={null}
let mut inserter = client.inserter("some")?
    .with_timeouts(Some(Duration::from_secs(5)), Some(Duration::from_secs(20)))
    .with_max_bytes(50_000_000)
    .with_max_rows(750_000)
    .with_period(Some(Duration::from_secs(15)));

inserter.write(&MyRow { no: 0, name: "foo".into() })?;
inserter.write(&MyRow { no: 1, name: "bar".into() })?;
let stats = inserter.commit().await?;
if stats.rows > 0 {
    println!(
        "{} bytes, {} rows, {} transactions have been inserted",
        stats.bytes, stats.rows, stats.transactions,
    );
}

// don't forget to finalize the inserter during the application shutdown
// and commit the remaining rows. `.end()` will provide stats as well.
inserter.end().await?;
```

* 如果达到任一阈值 (`max_bytes`、`max_rows`、`period`) ，`Inserter` 会在 `commit()` 中结束当前进行中的插入。
* 可以使用 `with_period_bias` 为结束活动 `INSERT` 之间的时间间隔引入偏移，从而避免并行 inserter 带来的负载尖峰。
* `Inserter::time_left()` 可用于判断当前周期何时结束。如果你的 stream 很少产生条目，请再次调用 `Inserter::commit()` 以检查限制条件。
* 时间阈值基于 [quanta](https://docs.rs/quanta) crate 实现，以提升 `inserter` 的性能。如果启用了 `test-util`，则不会使用它 (因此，在自定义测试中可通过 `tokio::time::advance()` 控制时间) 。
* 两次 `commit()` 调用之间的所有行都会插入到同一条 `INSERT` 语句中。

<Warning>
  如果你想终止/完成插入，别忘了执行 flush：

  ```rust theme={null}
  inserter.end().await?;
  ```
</Warning>

<div id="executing-ddls">
  ### 执行 DDL 语句
</div>

对于单节点部署，像这样执行 DDL 语句即可：

```rust theme={null}
client.query("DROP TABLE IF EXISTS some").execute().await?;
```

不过，对于带有负载均衡器的集群部署或 ClickHouse Cloud，建议使用 `wait_end_of_query` 选项，等待 DDL 在所有副本上全部生效。可以这样操作：

```rust theme={null}
client
    .query("DROP TABLE IF EXISTS some")
    .with_option("wait_end_of_query", "1")
    .execute()
    .await?;
```

<div id="clickhouse-settings">
  ### ClickHouse 设置
</div>

你可以使用 `with_option` 方法应用各种 [ClickHouse 设置](/docs/zh/reference/settings/session-settings)。例如：

```rust theme={null}
let numbers = client
    .query("SELECT number FROM system.numbers")
    // This setting will be applied to this particular query only;
    // it will override the global client setting.
    .with_option("limit", "3")
    .fetch_all::<u64>()
    .await?;
```

除 `query` 外，这种方式同样适用于 `insert` 和 `inserter` 方法；此外，也可以在 `Client` 实例上调用同一方法，为所有查询设置全局参数。

<div id="query-id">
  ### Query ID
</div>

使用 `.with_option` 可以设置 `query_id` 选项，以便在 ClickHouse 查询日志中识别查询。

```rust theme={null}
let numbers = client
    .query("SELECT number FROM system.numbers LIMIT 1")
    .with_option("query_id", "some-query-id")
    .fetch_all::<u64>()
    .await?;
```

除了 `query` 之外，它与 `insert` 和 `inserter` 方法的用法类似。

<Danger>
  如果你手动设置 `query_id`，请确保它是唯一的。为此，UUID 是不错的选择。
</Danger>

另请参阅：客户端仓库中的 [query\_id 示例](https://github.com/ClickHouse/clickhouse-rs/blob/main/examples/query_id.rs)。

<div id="session-id">
  ### 会话 ID
</div>

与 `query_id` 类似，您也可以设置 `session_id`，让这些语句在同一会话中执行。`session_id` 既可以在客户端级别进行全局设置，也可以针对每次 `query`、`insert` 或 `inserter` 调用单独设置。

```rust theme={null}
let client = Client::default()
    .with_url("http://localhost:8123")
    .with_option("session_id", "my-session");
```

<Danger>
  在集群部署场景下，由于缺少 "粘性会话"，你需要连接到*特定的集群节点*，才能正确使用此功能；例如，轮询负载均衡器无法保证后续请求会由同一个 ClickHouse 节点处理。
</Danger>

另请参见：客户端仓库中的 [session\_id 示例](https://github.com/ClickHouse/clickhouse-rs/blob/main/examples/session_id.rs)。

<div id="custom-http-headers">
  ### 自定义 HTTP 请求头
</div>

如果你使用代理进行身份验证，或者需要传递自定义请求头，可以按如下方式操作：

```rust theme={null}
let client = Client::default()
    .with_url("http://localhost:8123")
    .with_header("X-My-Header", "hello");
```

另请参见：客户端仓库中的[自定义 HTTP 请求头示例](https://github.com/ClickHouse/clickhouse-rs/blob/main/examples/custom_http_headers.rs)。

<div id="custom-http-client">
  ### 自定义 HTTP 客户端
</div>

这有助于调整底层 HTTP 连接池的设置。

```rust theme={null}
use hyper_util::client::legacy::connect::HttpConnector;
use hyper_util::client::legacy::Client as HyperClient;
use hyper_util::rt::TokioExecutor;

let connector = HttpConnector::new(); // or HttpsConnectorBuilder
let hyper_client = HyperClient::builder(TokioExecutor::new())
    // For how long keep a particular idle socket alive on the client side (in milliseconds).
    // It is supposed to be a fair bit less that the ClickHouse server KeepAlive timeout,
    // which was by default 3 seconds for pre-23.11 versions, and 10 seconds after that.
    .pool_idle_timeout(Duration::from_millis(2_500))
    // Sets the maximum idle Keep-Alive connections allowed in the pool.
    .pool_max_idle_per_host(4)
    .build(connector);

let client = Client::with_http_client(hyper_client).with_url("http://localhost:8123");
```

<Warning>
  此示例依赖旧版 Hyper API，后续可能会调整。
</Warning>

另请参阅客户端仓库中的[自定义 HTTP 客户端示例](https://github.com/ClickHouse/clickhouse-rs/blob/main/examples/custom_http_client.rs)。

<div id="data-types">
  ## 数据类型
</div>

<Info>
  另请参阅以下补充示例：

  * [更简单的 ClickHouse 数据类型](https://github.com/ClickHouse/clickhouse-rs/blob/main/examples/data_types_derive_simple.rs)
  * [容器类 ClickHouse 数据类型](https://github.com/ClickHouse/clickhouse-rs/blob/main/examples/data_types_derive_containers.rs)
</Info>

* `(U)Int(8|16|32|64|128)` 可与对应的 `(u|i)(8|16|32|64|128)` 类型或基于它们的 newtype 相互映射。
* `Int256` 和 `UInt256` 目前不支持直接映射，但可以使用[变通方案](https://github.com/ClickHouse/clickhouse-rs/issues/48)。
* `Float(32|64)` 可与对应的 `f(32|64)` 或基于它们的 newtype 相互映射。
* `Decimal(32|64|128)` 可与对应的 `i(32|64|128)` 或基于它们的 newtype 相互映射。使用 [`fixnum`](https://github.com/loyd/fixnum) 或其他有符号定点数实现会更方便。
* `Boolean` 可与 `bool` 或基于它的 newtype 相互映射。
* `String` 可与任意字符串或字节类型相互映射，例如 `&str`、`&[u8]`、`String`、`Vec<u8>` 或 [`SmartString`](https://docs.rs/smartstring/latest/smartstring/struct.SmartString.html)。也支持 newtype。若要存储字节，建议使用 [`serde_bytes`](https://docs.rs/serde_bytes/latest/serde_bytes/)，因为效率更高。

```rust theme={null}
#[derive(Row, Debug, Serialize, Deserialize)]
struct MyRow<'a> {
    str: &'a str,
    string: String,
    #[serde(with = "serde_bytes")]
    bytes: Vec<u8>,
    #[serde(with = "serde_bytes")]
    byte_slice: &'a [u8],
}
```

* 支持将 `FixedString(N)` 作为字节数组使用，例如 `[u8; N]`。

```rust theme={null}
#[derive(Row, Debug, Serialize, Deserialize)]
struct MyRow {
    fixed_str: [u8; 16], // FixedString(16)
}
```

* 可通过 [`serde_repr`](https://docs.rs/serde_repr/latest/serde_repr/) 支持 `Enum(8|16)`。

```rust theme={null}
use serde_repr::{Deserialize_repr, Serialize_repr};

#[derive(Row, Serialize, Deserialize)]
struct MyRow {
    level: Level,
}

#[derive(Debug, Serialize_repr, Deserialize_repr)]
#[repr(u8)]
enum Level {
    Debug = 1,
    Info = 2,
    Warn = 3,
    Error = 4,
}
```

* `UUID` 通过 `serde::uuid` 与 [`uuid::Uuid`](https://docs.rs/uuid/latest/uuid/struct.Uuid.html) 相互映射。需要启用 `uuid` feature。

```rust theme={null}
#[derive(Row, Serialize, Deserialize)]
struct MyRow {
    #[serde(with = "clickhouse::serde::uuid")]
    uuid: uuid::Uuid,
}
```

* `IPv6` 可与 [`std::net::Ipv6Addr`](https://doc.rust-lang.org/stable/std/net/struct.Ipv6Addr.html) 相互映射。
* `IPv4` 可通过 `serde::ipv4` 与 [`std::net::Ipv4Addr`](https://doc.rust-lang.org/stable/std/net/struct.Ipv4Addr.html) 相互映射。

```rust theme={null}
#[derive(Row, Serialize, Deserialize)]
struct MyRow {
    #[serde(with = "clickhouse::serde::ipv4")]
    ipv4: std::net::Ipv4Addr,
}
```

* `Date` 可映射为/从 `u16` 或其外层包装的 newtype，并表示自 `1970-01-01` 起经过的天数。此外，还支持 [`time::Date`](https://docs.rs/time/latest/time/struct.Date.html)，可通过使用 `serde::time::date` 实现，但这需要启用 `time` feature。

```rust theme={null}
#[derive(Row, Serialize, Deserialize)]
struct MyRow {
    days: u16,
    #[serde(with = "clickhouse::serde::time::date")]
    date: Date,
}
```

* `Date32` 可映射为/自 `i32` 或其外层的 `newtype`，表示自 `1970-01-01` 起经过的天数。此外，还支持通过 `serde::time::date32` 使用 [`time::Date`](https://docs.rs/time/latest/time/struct.Date.html)，这需要启用 `time` feature。

```rust theme={null}
#[derive(Row, Serialize, Deserialize)]
struct MyRow {
    days: i32,
    #[serde(with = "clickhouse::serde::time::date32")]
    date: Date,
}
```

* `DateTime` 可映射为/自 `u32` 或其外层封装的 newtype，表示自 UNIX 纪元以来经过的秒数。此外，还支持 [`time::OffsetDateTime`](https://docs.rs/time/latest/time/struct.OffsetDateTime.html)，可通过 `serde::time::datetime` 使用，但这需要启用 `time` feature。

```rust theme={null}
#[derive(Row, Serialize, Deserialize)]
struct MyRow {
    ts: u32,
    #[serde(with = "clickhouse::serde::time::datetime")]
    dt: OffsetDateTime,
}
```

* `DateTime64(_)` 可与 `i32` 或其外层包装的 `newtype` 相互映射，表示自 Unix epoch 起经过的时间。此外，还支持通过 `serde::time::datetime64::*` 使用 [`time::OffsetDateTime`](https://docs.rs/time/latest/time/struct.OffsetDateTime.html)，这需要启用 `time` feature。

```rust theme={null}
#[derive(Row, Serialize, Deserialize)]
struct MyRow {
    ts: i64, // elapsed s/us/ms/ns depending on `DateTime64(X)`
    #[serde(with = "clickhouse::serde::time::datetime64::secs")]
    dt64s: OffsetDateTime,  // `DateTime64(0)`
    #[serde(with = "clickhouse::serde::time::datetime64::millis")]
    dt64ms: OffsetDateTime, // `DateTime64(3)`
    #[serde(with = "clickhouse::serde::time::datetime64::micros")]
    dt64us: OffsetDateTime, // `DateTime64(6)`
    #[serde(with = "clickhouse::serde::time::datetime64::nanos")]
    dt64ns: OffsetDateTime, // `DateTime64(9)`
}
```

* `Tuple(A, B, ...)` 可映射为/从 `(A, B, ...)`，或映射为/从基于它的 `newtype`。
* `Array(_)` 可映射为/从任意 slice，例如 `Vec<_>`、`&[_]`。也支持自定义新类型。
* `Map(K, V)` 的行为类似于 `Array((K, V))`。
* `LowCardinality(_)` 可无缝支持。
* `Nullable(_)` 可映射为/从 `Option<_>`。对于 `clickhouse::serde::*` helpers，请添加 `::option`。

```rust theme={null}
#[derive(Row, Serialize, Deserialize)]
struct MyRow {
    #[serde(with = "clickhouse::serde::ipv4::option")]
    ipv4_opt: Option<Ipv4Addr>,
}
```

* 可通过提供多个重命名后的数组来支持 `Nested` 类型。

```rust theme={null}
// CREATE TABLE test(items Nested(name String, count UInt32))
#[derive(Row, Serialize, Deserialize)]
struct MyRow {
    #[serde(rename = "items.name")]
    items_name: Vec<String>,
    #[serde(rename = "items.count")]
    items_count: Vec<u32>,
}
```

* 支持 `Geo` 类型。`Point` 的行为类似于 `Tuple` `(f64, f64)`，其余类型都只是由点组成的切片。

```rust theme={null}
type Point = (f64, f64);
type Ring = Vec<Point>;
type Polygon = Vec<Ring>;
type MultiPolygon = Vec<Polygon>;
type LineString = Vec<Point>;
type MultiLineString = Vec<LineString>;

#[derive(Row, Serialize, Deserialize)]
struct MyRow {
    point: Point,
    ring: Ring,
    polygon: Polygon,
    multi_polygon: MultiPolygon,
    line_string: LineString,
    multi_line_string: MultiLineString,
}
```

* `Variant`、`Dynamic`、 (新的) `JSON` 数据类型暂不支持。

<div id="mocking">
  ## 模拟
</div>

该 crate 提供了一些工具，用于模拟 CH 服务器，以及测试 DDL、`SELECT`、`INSERT` 和 `WATCH` 查询。可通过 `test-util` feature 启用此功能。**仅**应将其用作开发依赖项。

参见[该示例](https://github.com/ClickHouse/clickhouse-rs/tree/main/examples/mock.rs)。

<div id="troubleshooting">
  ## 故障排查
</div>

<div id="cannot_read_all_data">
  ### CANNOT\_READ\_ALL\_DATA
</div>

`CANNOT_READ_ALL_DATA` 错误最常见的原因是，应用程序端的行定义与 ClickHouse 中的定义不一致。

考虑以下表：

```sql theme={null}
CREATE OR REPLACE TABLE event_log (id UInt32)
ENGINE = MergeTree
ORDER BY timestamp
```

然后，如果应用端定义的 `EventLog` 类型不匹配，例如：

```rust theme={null}
#[derive(Debug, Serialize, Deserialize, Row)]
struct EventLog {
    id: String, // <- should be u32 instead!
}
```

插入数据时，可能会出现以下错误：

```response theme={null}
Error: BadResponse("Code: 33. DB::Exception: Cannot read all data. Bytes read: 5. Bytes expected: 23.: (at row 1)\n: While executing BinaryRowInputFormat. (CANNOT_READ_ALL_DATA)")
```

在此示例中，可通过正确定义 `EventLog` struct 来修复此问题：

```rust theme={null}
#[derive(Debug, Serialize, Deserialize, Row)]
struct EventLog {
    id: u32
}
```

<div id="known-limitations">
  ## 已知限制
</div>

* 目前尚不支持 `Variant`、`Dynamic` 和 (新的) `JSON` 数据类型。
* 目前尚不支持服务器端参数绑定；跟踪进展请参见[此 issue](https://github.com/ClickHouse/clickhouse-rs/issues/142)。

<div id="contact-us">
  ## 联系我们
</div>

如果你有任何问题或需要帮助，欢迎通过 [Community Slack](https://clickhouse.com/slack) 或 [GitHub issues](https://github.com/ClickHouse/clickhouse-rs/issues) 与我们联系。
