{"id":15066728,"url":"https://github.com/clickhouse/clickhouse-rs","last_synced_at":"2025-04-11T17:23:46.082Z","repository":{"id":36964785,"uuid":"303940930","full_name":"ClickHouse/clickhouse-rs","owner":"ClickHouse","description":"Official pure Rust typed client for ClickHouse DB","archived":false,"fork":false,"pushed_at":"2025-03-17T07:39:06.000Z","size":606,"stargazers_count":369,"open_issues_count":56,"forks_count":106,"subscribers_count":11,"default_branch":"main","last_synced_at":"2025-04-04T11:39:14.960Z","etag":null,"topics":["clickhouse","http","rust","streaming","tokio"],"latest_commit_sha":null,"homepage":"https://clickhouse.com","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ClickHouse.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-10-14T07:47:25.000Z","updated_at":"2025-04-03T14:42:24.000Z","dependencies_parsed_at":"2024-01-27T17:25:32.642Z","dependency_job_id":"460f915a-4f14-4e85-b01c-dfe9d76664c4","html_url":"https://github.com/ClickHouse/clickhouse-rs","commit_stats":{"total_commits":323,"total_committers":16,"mean_commits":20.1875,"dds":0.08049535603715174,"last_synced_commit":"ed74a4eeb3475c607811d22c1eb605e7f008849a"},"previous_names":["loyd/typed-clickhouse","clickhouse/clickhouse.rs","clickhouse/clickhouse-rs","loyd/clickhouse.rs"],"tags_count":38,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ClickHouse%2Fclickhouse-rs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ClickHouse%2Fclickhouse-rs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ClickHouse%2Fclickhouse-rs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ClickHouse%2Fclickhouse-rs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ClickHouse","download_url":"https://codeload.github.com/ClickHouse/clickhouse-rs/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248448264,"owners_count":21105266,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clickhouse","http","rust","streaming","tokio"],"created_at":"2024-09-25T01:11:24.020Z","updated_at":"2025-04-11T17:23:46.045Z","avatar_url":"https://github.com/ClickHouse.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# clickhouse-rs\n\nOfficial pure Rust typed client for ClickHouse DB.\n\n[![Crates.io][crates-badge]][crates-url]\n[![Documentation][docs-badge]][docs-url]\n[![License][license-badge]][license-url]\n[![Build Status][actions-badge]][actions-url]\n\n[crates-badge]: https://img.shields.io/crates/v/clickhouse.svg\n[crates-url]: https://crates.io/crates/clickhouse\n[docs-badge]: https://docs.rs/clickhouse/badge.svg\n[docs-url]: https://docs.rs/clickhouse\n[license-badge]: https://img.shields.io/badge/license-MIT_OR_Apache--2.0-blue.svg\n[license-url]: https://github.com/ClickHouse/clickhouse-rs/blob/main/LICENSE-MIT\n[actions-badge]: https://github.com/ClickHouse/clickhouse-rs/actions/workflows/ci.yml/badge.svg\n[actions-url]: https://github.com/ClickHouse/clickhouse-rs/actions/workflows/ci.yml\n\n* Uses `serde` for encoding/decoding rows.\n* Supports `serde` attributes: `skip_serializing`, `skip_deserializing`, `rename`.\n* Uses `RowBinary` encoding over HTTP transport.\n    * There are plans to switch to `Native` over TCP.\n* Supports TLS (see `native-tls` and `rustls-tls` features below).\n* Supports compression and decompression (LZ4 and LZ4HC).\n* Provides API for selecting.\n* Provides API for inserting.\n* Provides API for infinite transactional (see below) inserting.\n* Provides API for watching live views.\n* Provides mocks for unit testing.\n\nNote: [ch2rs](https://github.com/ClickHouse/ch2rs) is useful to generate a row type from ClickHouse.\n\n## Usage\n\nTo use the crate, add this to your `Cargo.toml`:\n```toml\n[dependencies]\nclickhouse = \"0.13.2\"\n\n[dev-dependencies]\nclickhouse = { version = \"0.13.2\", features = [\"test-util\"] }\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e\n\n### Note about ClickHouse prior to v22.6\n\n\u003c/summary\u003e\n\nCH server older than v22.6 (2022-06-16) handles `RowBinary` [incorrectly](https://github.com/ClickHouse/ClickHouse/issues/37420) in some rare cases. Use 0.11 and enable `wa-37420` feature to solve this problem. Don't use it for newer versions.\n\n\u003c/details\u003e\n\u003cdetails\u003e\n\u003csummary\u003e\n\n### Create a client\n\n\u003c/summary\u003e\n\n```rust,ignore\nuse clickhouse::Client;\n\nlet client = Client::default()\n    .with_url(\"http://localhost:8123\")\n    .with_user(\"name\")\n    .with_password(\"123\")\n    .with_database(\"test\");\n```\n\n* Reuse created clients or clone them in order to reuse a connection pool.\n\n\u003c/details\u003e\n\u003cdetails\u003e\n\u003csummary\u003e\n\n### Select rows\n\n\u003c/summary\u003e\n\n```rust,ignore\nuse serde::Deserialize;\nuse clickhouse::Row;\n\n#[derive(Row, Deserialize)]\nstruct MyRow\u003c'a\u003e {\n    no: u32,\n    name: \u0026'a str,\n}\n\nlet mut cursor = client\n    .query(\"SELECT ?fields FROM some WHERE no BETWEEN ? AND ?\")\n    .bind(500)\n    .bind(504)\n    .fetch::\u003cMyRow\u003c'_\u003e\u003e()?;\n\nwhile let Some(row) = cursor.next().await? { .. }\n```\n\n* Placeholder `?fields` is replaced with `no, name` (fields of `Row`).\n* Placeholder `?` is replaced with values in following `bind()` calls.\n* Convenient `fetch_one::\u003cRow\u003e()` and `fetch_all::\u003cRow\u003e()` can be used to get a first row or all rows correspondingly.\n* `sql::Identifier` can be used to bind table names.\n\nNote that cursors can return an error even after producing some rows. To avoid this, use `client.with_option(\"wait_end_of_query\", \"1\")` in order to enable buffering on the server-side. [More details](https://clickhouse.com/docs/en/interfaces/http/#response-buffering). The `buffer_size` option can be useful too.\n\n\u003c/details\u003e\n\u003cdetails\u003e\n\u003csummary\u003e\n\n### Insert a batch\n\n\u003c/summary\u003e\n\n```rust,ignore\nuse serde::Serialize;\nuse clickhouse::Row;\n\n#[derive(Row, Serialize)]\nstruct MyRow {\n    no: u32,\n    name: String,\n}\n\nlet mut insert = client.insert(\"some\")?;\ninsert.write(\u0026MyRow { no: 0, name: \"foo\".into() }).await?;\ninsert.write(\u0026MyRow { no: 1, name: \"bar\".into() }).await?;\ninsert.end().await?;\n```\n\n* If `end()` isn't called, the `INSERT` is aborted.\n* Rows are being sent progressively to spread network load.\n* ClickHouse inserts batches atomically only if all rows fit in the same partition and their number is less [`max_insert_block_size`](https://clickhouse.com/docs/en/operations/settings/settings#max_insert_block_size).\n\n\u003c/details\u003e\n\u003cdetails\u003e\n\u003csummary\u003e\n\n### Infinite inserting\n\n\u003c/summary\u003e\n\nRequires the `inserter` feature.\n\n```rust,ignore\nlet mut inserter = client.inserter(\"some\")?\n    .with_timeouts(Some(Duration::from_secs(5)), Some(Duration::from_secs(20)))\n    .with_max_bytes(50_000_000)\n    .with_max_rows(750_000)\n    .with_period(Some(Duration::from_secs(15)));\n\ninserter.write(\u0026MyRow { no: 0, name: \"foo\".into() })?;\ninserter.write(\u0026MyRow { no: 1, name: \"bar\".into() })?;\nlet stats = inserter.commit().await?;\nif stats.rows \u003e 0 {\n    println!(\n        \"{} bytes, {} rows, {} transactions have been inserted\",\n        stats.bytes, stats.rows, stats.transactions,\n    );\n}\n```\n\nPlease, read [examples](https://github.com/ClickHouse/clickhouse-rs/tree/main/examples/inserter.rs) to understand how to use it properly in different real-world cases.\n\n* `Inserter` ends an active insert in `commit()` if thresholds (`max_bytes`, `max_rows`, `period`) are reached.\n* The interval between ending active `INSERT`s can be biased by using `with_period_bias` to avoid load spikes by parallel inserters.\n* `Inserter::time_left()` can be used to detect when the current period ends. Call `Inserter::commit()` again to check limits if your stream emits items rarely.\n* Time thresholds implemented by using [quanta](https://docs.rs/quanta) crate to speed the inserter up. Not used if `test-util` is enabled (thus, time can be managed by `tokio::time::advance()` in custom tests).\n* All rows between `commit()` calls are inserted in the same `INSERT` statement.\n* Do not forget to flush if you want to terminate inserting:\n```rust,ignore\ninserter.end().await?;\n```\n\n\u003c/details\u003e\n\u003cdetails\u003e\n\u003csummary\u003e\n\n### Perform DDL\n\n\u003c/summary\u003e\n\n```rust,ignore\nclient.query(\"DROP TABLE IF EXISTS some\").execute().await?;\n```\n\n\u003c/details\u003e\n\u003cdetails\u003e\n\u003csummary\u003e\n\n### Live views\n\n\u003c/summary\u003e\n\nRequires the `watch` feature.\n\n```rust,ignore\nlet mut cursor = client\n    .watch(\"SELECT max(no), argMax(name, no) FROM some\")\n    .fetch::\u003cRow\u003c'_\u003e\u003e()?;\n\nlet (version, row) = cursor.next().await?.unwrap();\nprintln!(\"live view updated: version={}, row={:?}\", version, row);\n\n// Use `only_events()` to iterate over versions only.\nlet mut cursor = client.watch(\"some_live_view\").limit(20).only_events().fetch()?;\nprintln!(\"live view updated: version={:?}\", cursor.next().await?);\n```\n\n* Use [carefully](https://github.com/ClickHouse/ClickHouse/issues/28309#issuecomment-908666042).\n* This code uses or creates if not exists a temporary live view named `lv_{sha1(query)}` to reuse the same live view by parallel watchers.\n* You can specify a name instead of a query.\n* This API uses `JSONEachRowWithProgress` under the hood because of [the issue](https://github.com/ClickHouse/ClickHouse/issues/22996).\n* Only struct rows can be used. Avoid `fetch::\u003cu64\u003e()` and other without specified names.\n\n\u003c/details\u003e\n\nSee [examples](https://github.com/ClickHouse/clickhouse-rs/tree/main/examples).\n\n## Feature Flags\n* `lz4` (enabled by default) — enables `Compression::Lz4`. If enabled, `Compression::Lz4` is used by default for all queries except for `WATCH`.\n* `inserter` — enables `client.inserter()`.\n* `test-util` — adds mocks. See [the example](https://github.com/ClickHouse/clickhouse-rs/tree/main/examples/mock.rs). Use it only in `dev-dependencies`.\n* `watch` — enables `client.watch` functionality. See the corresponding section for details.\n* `uuid` — adds `serde::uuid` to work with [uuid](https://docs.rs/uuid) crate.\n* `time` — adds `serde::time` to work with [time](https://docs.rs/time) crate.\n* `chrono` — adds `serde::chrono` to work with [chrono](https://docs.rs/chrono) crate.\n\n### TLS\nBy default, TLS is disabled and one or more following features must be enabled to use HTTPS urls:\n* `native-tls` — uses [native-tls], utilizing dynamic linking (e.g. against OpenSSL).\n* `rustls-tls` — enables `rustls-tls-aws-lc` and `rustls-tls-webpki-roots` features.\n* `rustls-tls-aws-lc` — uses [rustls] with the `aws-lc` cryptography implementation.\n* `rustls-tls-ring` — uses [rustls] with the `ring` cryptography implementation.\n* `rustls-tls-webpki-roots` — uses [rustls] with certificates provided by the [webpki-roots] crate.\n* `rustls-tls-native-roots` — uses [rustls] with certificates provided by the [rustls-native-certs] crate.\n\nIf multiple features are enabled, the following priority is applied:\n* `native-tls` \u003e `rustls-tls-aws-lc` \u003e `rustls-tls-ring`\n* `rustls-tls-native-roots` \u003e `rustls-tls-webpki-roots`\n\nHow to choose between all these features? Here are some considerations:\n* A good starting point is `rustls-tls`, e.g. if you use ClickHouse Cloud.\n* To be more environment-agnostic, prefer `rustls-tls` over `native-tls`.\n* Enable `rustls-tls-native-roots` or `native-tls` if you want to use self-signed certificates.\n\n[native-tls]: https://docs.rs/native-tls\n[rustls]: https://docs.rs/rustls\n[webpki-roots]: https://docs.rs/webpki-roots\n[rustls-native-certs]: https://docs.rs/rustls-native-certs\n\n## Data Types\n* `(U)Int(8|16|32|64|128)` maps to/from corresponding `(u|i)(8|16|32|64|128)` types or newtypes around them.\n* `(U)Int256` aren't supported directly, but there is [a workaround for it](https://github.com/ClickHouse/clickhouse-rs/issues/48).\n* `Float(32|64)` maps to/from corresponding `f(32|64)` or newtypes around them.\n* `Decimal(32|64|128)` maps to/from corresponding `i(32|64|128)` or newtypes around them. It's more convenient to use [fixnum](https://github.com/loyd/fixnum) or another implementation of signed fixed-point numbers.\n* `Boolean` maps to/from `bool` or newtypes around it.\n* `String` maps to/from any string or bytes types, e.g. `\u0026str`, `\u0026[u8]`, `String`, `Vec\u003cu8\u003e` or [`SmartString`](https://docs.rs/smartstring/latest/smartstring/struct.SmartString.html). Newtypes are also supported. To store bytes, consider using [serde_bytes](https://docs.rs/serde_bytes/latest/serde_bytes/), because it's more efficient.\n    \u003cdetails\u003e\n    \u003csummary\u003eExample\u003c/summary\u003e\n\n    ```rust,ignore\n    #[derive(Row, Debug, Serialize, Deserialize)]\n    struct MyRow\u003c'a\u003e {\n        str: \u0026'a str,\n        string: String,\n        #[serde(with = \"serde_bytes\")]\n        bytes: Vec\u003cu8\u003e,\n        #[serde(with = \"serde_bytes\")]\n        byte_slice: \u0026'a [u8],\n    }\n    ```\n    \u003c/details\u003e\n* `FixedString(N)` is supported as an array of bytes, e.g. `[u8; N]`.\n    \u003cdetails\u003e\n    \u003csummary\u003eExample\u003c/summary\u003e\n  \n    ```rust,ignore\n    #[derive(Row, Debug, Serialize, Deserialize)]\n    struct MyRow {\n        fixed_str: [u8; 16], // FixedString(16)\n    }\n    ```\n    \u003c/details\u003e\n* `Enum(8|16)` are supported using [serde_repr](https://docs.rs/serde_repr/latest/serde_repr/).\n    \u003cdetails\u003e\n    \u003csummary\u003eExample\u003c/summary\u003e\n\n    ```rust,ignore\n    use serde_repr::{Deserialize_repr, Serialize_repr};\n\n    #[derive(Row, Serialize, Deserialize)]\n    struct MyRow {\n        level: Level,\n    }\n\n    #[derive(Debug, Serialize_repr, Deserialize_repr)]\n    #[repr(u8)]\n    enum Level {\n        Debug = 1,\n        Info = 2,\n        Warn = 3,\n        Error = 4,\n    }\n    ```\n    \u003c/details\u003e\n* `UUID` maps to/from [`uuid::Uuid`](https://docs.rs/uuid/latest/uuid/struct.Uuid.html) by using `serde::uuid`. Requires the `uuid` feature.\n    \u003cdetails\u003e\n    \u003csummary\u003eExample\u003c/summary\u003e\n\n    ```rust,ignore\n    #[derive(Row, Serialize, Deserialize)]\n    struct MyRow {\n        #[serde(with = \"clickhouse::serde::uuid\")]\n        uuid: uuid::Uuid,\n    }\n    ```\n    \u003c/details\u003e\n* `IPv6` maps to/from [`std::net::Ipv6Addr`](https://doc.rust-lang.org/stable/std/net/struct.Ipv6Addr.html).\n* `IPv4` maps to/from [`std::net::Ipv4Addr`](https://doc.rust-lang.org/stable/std/net/struct.Ipv4Addr.html) by using `serde::ipv4`.\n    \u003cdetails\u003e\n    \u003csummary\u003eExample\u003c/summary\u003e\n\n    ```rust,ignore\n    #[derive(Row, Serialize, Deserialize)]\n    struct MyRow {\n        #[serde(with = \"clickhouse::serde::ipv4\")]\n        ipv4: std::net::Ipv4Addr,\n    }\n    ```\n    \u003c/details\u003e\n* `Date` maps to/from `u16` or a newtype around it and represents a number of days elapsed since `1970-01-01`. The following external types are supported: \n    * [`time::Date`](https://docs.rs/time/latest/time/struct.Date.html) is supported by using `serde::time::date`, requiring the `time` feature. \n    * [`chrono::NaiveDate`](https://docs.rs/chrono/latest/chrono/struct.NaiveDate.html) is supported by using `serde::chrono::date`, requiring the `chrono` feature. \n    \u003cdetails\u003e\n    \u003csummary\u003eExample\u003c/summary\u003e\n\n    ```rust,ignore\n    #[derive(Row, Serialize, Deserialize)]\n    struct MyRow {\n        days: u16,\n        #[serde(with = \"clickhouse::serde::time::date\")]\n        date: Date,\n        // if you prefer using chrono:\n        #[serde(with = \"clickhouse::serde::chrono::date\")]\n        date_chrono: NaiveDate,\n    }\n\n    ```\n    \u003c/details\u003e\n* `Date32` maps to/from `i32` or a newtype around it and represents a number of days elapsed since `1970-01-01`. The following external types are supported: \n    * [`time::Date`](https://docs.rs/time/latest/time/struct.Date.html) is supported by using `serde::time::date32`, requiring the `time` feature. \n    * [`chrono::NaiveDate`](https://docs.rs/chrono/latest/chrono/struct.NaiveDate.html) is supported by using `serde::chrono::date32`, requiring the `chrono` feature. \n    \u003cdetails\u003e\n    \u003csummary\u003eExample\u003c/summary\u003e\n\n    ```rust,ignore\n    #[derive(Row, Serialize, Deserialize)]\n    struct MyRow {\n        days: i32,\n        #[serde(with = \"clickhouse::serde::time::date32\")]\n        date: Date,\n        // if you prefer using chrono:\n        #[serde(with = \"clickhouse::serde::chrono::date32\")]\n        date_chrono: NaiveDate,\n\n    }\n\n    ```\n    \u003c/details\u003e\n* `DateTime` maps to/from `u32` or a newtype around it and represents a number of seconds elapsed since UNIX epoch. The following external types are supported:\n    * [`time::OffsetDateTime`](https://docs.rs/time/latest/time/struct.OffsetDateTime.html) is supported by using `serde::time::datetime`, requiring the `time` feature. \n    * [`chrono::DateTime\u003cUtc\u003e`](https://docs.rs/chrono/latest/chrono/struct.DateTime.html) is supported by using `serde::chrono::datetime`, requiring the `chrono` feature. \n    \u003cdetails\u003e\n    \u003csummary\u003eExample\u003c/summary\u003e\n\n    ```rust,ignore\n    #[derive(Row, Serialize, Deserialize)]\n    struct MyRow {\n        ts: u32,\n        #[serde(with = \"clickhouse::serde::time::datetime\")]\n        dt: OffsetDateTime,\n        // if you prefer using chrono:\n        #[serde(with = \"clickhouse::serde::chrono::datetime\")]\n        dt_chrono: DateTime\u003cUtc\u003e,        \n    }\n    ```\n    \u003c/details\u003e\n* `DateTime64(_)` maps to/from `i64` or a newtype around it and represents a time elapsed since UNIX epoch. The following external types are supported:\n    * [`time::OffsetDateTime`](https://docs.rs/time/latest/time/struct.OffsetDateTime.html) is supported by using `serde::time::datetime64::*`, requiring the `time` feature. \n    * [`chrono::DateTime\u003cUtc\u003e`](https://docs.rs/chrono/latest/chrono/struct.DateTime.html) is supported by using `serde::chrono::datetime64::*`, requiring the `chrono` feature. \n    \u003cdetails\u003e\n    \u003csummary\u003eExample\u003c/summary\u003e\n\n    ```rust,ignore\n    #[derive(Row, Serialize, Deserialize)]\n    struct MyRow {\n        ts: i64, // elapsed s/us/ms/ns depending on `DateTime64(X)`\n        #[serde(with = \"clickhouse::serde::time::datetime64::secs\")]\n        dt64s: OffsetDateTime,  // `DateTime64(0)`\n        #[serde(with = \"clickhouse::serde::time::datetime64::millis\")]\n        dt64ms: OffsetDateTime, // `DateTime64(3)`\n        #[serde(with = \"clickhouse::serde::time::datetime64::micros\")]\n        dt64us: OffsetDateTime, // `DateTime64(6)`\n        #[serde(with = \"clickhouse::serde::time::datetime64::nanos\")]\n        dt64ns: OffsetDateTime, // `DateTime64(9)`\n        // if you prefer using chrono:\n        #[serde(with = \"clickhouse::serde::chrono::datetime64::secs\")]\n        dt64s_chrono: DateTime\u003cUtc\u003e,  // `DateTime64(0)`\n        #[serde(with = \"clickhouse::serde::chrono::datetime64::millis\")]\n        dt64ms_chrono: DateTime\u003cUtc\u003e, // `DateTime64(3)`\n        #[serde(with = \"clickhouse::serde::chrono::datetime64::micros\")]\n        dt64us_chrono: DateTime\u003cUtc\u003e, // `DateTime64(6)`\n        #[serde(with = \"clickhouse::serde::chrono::datetime64::nanos\")]\n        dt64ns_chrono: DateTime\u003cUtc\u003e, // `DateTime64(9)`\n    }\n\n\n    ```\n    \u003c/details\u003e\n* `Tuple(A, B, ...)` maps to/from `(A, B, ...)` or a newtype around it.\n* `Array(_)` maps to/from any slice, e.g. `Vec\u003c_\u003e`, `\u0026[_]`. Newtypes are also supported.\n* `Map(K, V)` behaves like `Array((K, V))`.\n* `LowCardinality(_)` is supported seamlessly.\n* `Nullable(_)` maps to/from `Option\u003c_\u003e`. For `clickhouse::serde::*` helpers add `::option`.\n    \u003cdetails\u003e\n    \u003csummary\u003eExample\u003c/summary\u003e\n\n    ```rust,ignore\n    #[derive(Row, Serialize, Deserialize)]\n    struct MyRow {\n        #[serde(with = \"clickhouse::serde::ipv4::option\")]\n        ipv4_opt: Option\u003cIpv4Addr\u003e,\n    }\n    ```\n    \u003c/details\u003e\n* `Nested` is supported by providing multiple arrays with renaming.\n    \u003cdetails\u003e\n    \u003csummary\u003eExample\u003c/summary\u003e\n\n    ```rust,ignore\n    // CREATE TABLE test(items Nested(name String, count UInt32))\n    #[derive(Row, Serialize, Deserialize)]\n    struct MyRow {\n        #[serde(rename = \"items.name\")]\n        items_name: Vec\u003cString\u003e,\n        #[serde(rename = \"items.count\")]\n        items_count: Vec\u003cu32\u003e,\n    }\n    ```\n    \u003c/details\u003e\n* `Geo` types are supported. `Point` behaves like a tuple `(f64, f64)`, and the rest of the types are just slices of points. \n    \u003cdetails\u003e\n    \u003csummary\u003eExample\u003c/summary\u003e\n\n    ```rust,ignore\n    type Point = (f64, f64);\n    type Ring = Vec\u003cPoint\u003e;\n    type Polygon = Vec\u003cRing\u003e;\n    type MultiPolygon = Vec\u003cPolygon\u003e;\n    type LineString = Vec\u003cPoint\u003e;\n    type MultiLineString = Vec\u003cLineString\u003e;\n  \n    #[derive(Row, Serialize, Deserialize)]\n    struct MyRow {\n        point: Point,\n        ring: Ring,\n        polygon: Polygon,\n        multi_polygon: MultiPolygon,\n        line_string: LineString,\n        multi_line_string: MultiLineString,\n    }\n    ```\n    \u003c/details\u003e\n* `Variant` data type is supported as a Rust enum. As the inner Variant types are _always_ sorted alphabetically, Rust enum variants should be defined in the _exactly_ same order as it is in the data type; their names are irrelevant, only the order of the types matters. This following example has a column defined as `Variant(Array(UInt16), Bool, Date, String, UInt32)`:\n    \u003cdetails\u003e\n    \u003csummary\u003eExample\u003c/summary\u003e\n    \n    ```rust,ignore\n    #[derive(Serialize, Deserialize)]\n    enum MyRowVariant {\n        Array(Vec\u003ci16\u003e),\n        Boolean(bool),\n        #[serde(with = \"clickhouse::serde::time::date\")]\n        Date(time::Date),\n        String(String),\n        UInt32(u32),\n    }\n    \n    #[derive(Row, Serialize, Deserialize)]\n    struct MyRow {\n        id: u64,\n        var: MyRowVariant,\n    }\n    ```\n    \u003c/details\u003e\n* [New `JSON` data type](https://clickhouse.com/docs/en/sql-reference/data-types/newjson) is currently supported as a string when using ClickHouse 24.10+. See [this example](examples/data_types_new_json.rs) for more details.\n* `Dynamic` data type is not supported for now.\n\nSee also the additional examples:\n\n* [Simpler ClickHouse data types](examples/data_types_derive_simple.rs)\n* [Container-like ClickHouse data types](examples/data_types_derive_containers.rs)\n* [Variant data type](examples/data_types_variant.rs)\n\n## Mocking\nThe crate provides utils for mocking CH server and testing DDL, `SELECT`, `INSERT` and `WATCH` queries.\n\nThe functionality can be enabled with the `test-util` feature. Use it **only** in dev-dependencies.\n\nSee [the example](https://github.com/ClickHouse/clickhouse-rs/tree/main/examples/mock.rs).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclickhouse%2Fclickhouse-rs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fclickhouse%2Fclickhouse-rs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclickhouse%2Fclickhouse-rs/lists"}