Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Data types examples #128

Merged
merged 8 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ required-features = ["test-util"]
name = "clickhouse_cloud"
required-features = ["rustls-tls"]

[[example]]
name = "data_types_derive_simple"
required-features = ["time", "uuid"]

[profile.release]
debug = true

Expand Down Expand Up @@ -95,4 +99,5 @@ serde_bytes = "0.11.4"
serde_repr = "0.1.7"
uuid = { version = "1", features = ["v4"] }
time = { version = "0.3.17", features = ["macros", "rand"] }
fixnum = { version = "0.9.2", features = ["serde", "i32", "i64", "i128"] }
rand = { version = "0.8.5", features = ["small_rng"] }
18 changes: 16 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,17 @@ See [examples](https://github.com/ClickHouse/clickhouse-rs/tree/main/examples).
}
```
</details>
* `FixedString(_)` isn't [supported yet](https://github.com/ClickHouse/clickhouse-rs/issues/49).
* `FixedString(N)` is supported as an array of bytes, e.g. `[u8; N]`.
<details>
<summary>Example</summary>

```rust,ignore
#[derive(Row, Debug, Serialize, Deserialize)]
struct MyRow {
fixed_str: [u8; 16], // FixedString(16)
}
```
</details>
* `Enum(8|16)` are supported using [serde_repr](https://docs.rs/serde_repr/latest/serde_repr/).
<details>
<summary>Example</summary>
Expand Down Expand Up @@ -357,7 +367,7 @@ See [examples](https://github.com/ClickHouse/clickhouse-rs/tree/main/examples).
}
```
</details>
* `Typle(A, B, ...)` maps to/from `(A, B, ...)` or a newtype around it.
* `Tuple(A, B, ...)` maps to/from `(A, B, ...)` or a newtype around it.
* `Array(_)` maps to/from any slice, e.g. `Vec<_>`, `&[_]`. Newtypes are also supported.
* `Map(K, V)` behaves like `Array((K, V))`.
* `LowCardinality(_)` is supported seamlessly.
Expand Down Expand Up @@ -390,6 +400,10 @@ See [examples](https://github.com/ClickHouse/clickhouse-rs/tree/main/examples).
</details>
* `JSON` and `Geo` aren't supported for now.

See also the additional examples:

* [Simpler ClickHouse data types](examples/data_types_derive_simple.rs)

## Mocking
The crate provides utils for mocking CH server and testing DDL, `SELECT`, `INSERT` and `WATCH` queries.

Expand Down
4 changes: 4 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ If something is missing, or you found a mistake in one of these examples, please
- [clickhouse_cloud.rs](clickhouse_cloud.rs) - using the client with ClickHouse Cloud, highlighting a few relevant settings (`wait_end_of_query`, `select_sequential_consistency`). Cargo features: requires `rustls-tls`; the code also works with `native-tls`.
- [clickhouse_settings.rs](clickhouse_settings.rs) - applying various ClickHouse settings on the query level

### Data types

- [data_types_derive_simple.rs](data_types_derive_simple.rs) - deriving simpler ClickHouse data types in a struct. Required cargo features: `time`, `uuid`.

### Special cases

- [custom_http_client.rs](custom_http_client.rs) - using a custom Hyper client, tweaking its connection pool settings
Expand Down
259 changes: 259 additions & 0 deletions examples/data_types_derive_simple.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
use std::str::FromStr;

use fixnum::typenum::{U12, U4, U8};
use fixnum::FixedPoint;
use rand::distributions::Alphanumeric;
use rand::seq::SliceRandom;
use rand::Rng;
use time::{Date, Month, OffsetDateTime, Time};

use clickhouse::sql::Identifier;
use clickhouse::{error::Result, Client};

// This example covers derivation of _simpler_ ClickHouse data types.
// See also: https://clickhouse.com/docs/en/sql-reference/data-types

#[tokio::main]
async fn main() -> Result<()> {
let table_name = "chrs_data_types_derive";
let client = Client::default().with_url("http://localhost:8123");

client
.query(
"
CREATE OR REPLACE TABLE ?
(
int8 Int8,
int16 Int16,
int32 Int32,
int64 Int64,
int128 Int128,
-- int256 Int256,
uint8 UInt8,
uint16 UInt16,
uint32 UInt32,
uint64 UInt64,
uint128 UInt128,
-- uint256 UInt256,
float32 Float32,
float64 Float64,
boolean Boolean,
str String,
blob_str String,
nullable_str Nullable(String),
low_car_str LowCardinality(String),
nullable_low_car_str LowCardinality(Nullable(String)),
fixed_str FixedString(16),
uuid UUID,
ipv4 IPv4,
ipv6 IPv6,
enum8 Enum8('Foo', 'Bar'),
enum16 Enum16('Qaz' = 42, 'Qux' = 255),
decimal32_9_4 Decimal(9, 4),
decimal64_18_8 Decimal(18, 8),
decimal128_38_12 Decimal(38, 12),
-- decimal256_76_20 Decimal(76, 20),
date Date,
date32 Date32,
datetime DateTime,
datetime_tz DateTime('UTC'),
datetime64_0 DateTime64(0),
datetime64_3 DateTime64(3),
datetime64_6 DateTime64(6),
datetime64_9 DateTime64(9),
datetime64_9_tz DateTime64(9, 'UTC')
) ENGINE MergeTree ORDER BY ();
",
)
.bind(Identifier(table_name))
.execute()
.await?;

let mut insert = client.insert(table_name)?;
insert.write(&Row::new()).await?;
insert.end().await?;

let rows = client
.query("SELECT ?fields FROM ?")
.bind(Identifier(table_name))
.fetch_all::<Row>()
.await?;

println!("{rows:#?}");
Ok(())
}

#[derive(Clone, Debug, PartialEq)]
#[derive(clickhouse::Row, serde::Serialize, serde::Deserialize)]
pub struct Row {
pub int8: i8,
pub int16: i16,
pub int32: i32,
pub int64: i64,
pub int128: i128,
pub uint8: u8,
pub uint16: u16,
pub uint32: u32,
pub uint64: u64,
pub uint128: u128,
pub float32: f32,
pub float64: f64,
pub boolean: bool,
pub str: String,
// Avoiding reading/writing strings as UTF-8 for blobs stored in a string column
#[serde(with = "serde_bytes")]
pub blob_str: Vec<u8>,
pub nullable_str: Option<String>,
// LowCardinality does not affect the struct definition
pub low_car_str: String,
// The same applies to a "nested" LowCardinality
pub nullable_low_car_str: Option<String>,
// FixedString is represented as raw bytes (similarly to `blob_str`, no UTF-8)
pub fixed_str: [u8; 16],
#[serde(with = "clickhouse::serde::uuid")]
pub uuid: uuid::Uuid,
#[serde(with = "clickhouse::serde::ipv4")]
pub ipv4: std::net::Ipv4Addr,
pub ipv6: std::net::Ipv6Addr,
pub enum8: Enum8,
pub enum16: Enum16,
pub decimal32_9_4: Decimal32,
pub decimal64_18_8: Decimal64,
pub decimal128_38_12: Decimal128,
#[serde(with = "clickhouse::serde::time::date")]
pub date: Date,
#[serde(with = "clickhouse::serde::time::date32")]
pub date32: Date,
#[serde(with = "clickhouse::serde::time::datetime")]
pub datetime: OffsetDateTime,
#[serde(with = "clickhouse::serde::time::datetime")]
pub datetime_tz: OffsetDateTime,
#[serde(with = "clickhouse::serde::time::datetime64::secs")]
pub datetime64_0: OffsetDateTime,
#[serde(with = "clickhouse::serde::time::datetime64::millis")]
pub datetime64_3: OffsetDateTime,
#[serde(with = "clickhouse::serde::time::datetime64::micros")]
pub datetime64_6: OffsetDateTime,
#[serde(with = "clickhouse::serde::time::datetime64::nanos")]
pub datetime64_9: OffsetDateTime,
#[serde(with = "clickhouse::serde::time::datetime64::nanos")]
pub datetime64_9_tz: OffsetDateTime,
}

// See ClickHouse decimal sizes: https://clickhouse.com/docs/en/sql-reference/data-types/decimal
type Decimal32 = FixedPoint<i32, U4>; // Decimal(9, 4) = Decimal32(4)
type Decimal64 = FixedPoint<i64, U8>; // Decimal(18, 8) = Decimal64(8)
type Decimal128 = FixedPoint<i128, U12>; // Decimal(38, 12) = Decimal128(12)

#[derive(Clone, Debug, PartialEq)]
#[derive(serde_repr::Serialize_repr, serde_repr::Deserialize_repr)]
#[repr(u8)]
pub enum Enum8 {
Foo = 1,
Bar = 2,
}

#[derive(Clone, Debug, PartialEq)]
#[derive(serde_repr::Serialize_repr, serde_repr::Deserialize_repr)]
#[repr(u16)]
pub enum Enum16 {
Qaz = 42,
Qux = 255,
}

impl Row {
pub fn new() -> Self {
let mut rng = rand::thread_rng();
Row {
int8: rng.gen(),
int16: rng.gen(),
int32: rng.gen(),
int64: rng.gen(),
int128: rng.gen(),
uint8: rng.gen(),
uint16: rng.gen(),
uint32: rng.gen(),
uint64: rng.gen(),
uint128: rng.gen(),
float32: rng.gen(),
float64: rng.gen(),
boolean: rng.gen(),
str: random_str(),
blob_str: rng.gen::<[u8; 3]>().to_vec(),
nullable_str: Some(random_str()),
low_car_str: random_str(),
nullable_low_car_str: Some(random_str()),
fixed_str: rng.gen(),
slvrtrn marked this conversation as resolved.
Show resolved Hide resolved
uuid: uuid::Uuid::new_v4(),
ipv4: std::net::Ipv4Addr::from_str("172.195.0.1").unwrap(),
ipv6: std::net::Ipv6Addr::from_str("::ffff:acc3:1").unwrap(),
enum8: [Enum8::Foo, Enum8::Bar]
.choose(&mut rng)
.unwrap()
.to_owned(),
enum16: [Enum16::Qaz, Enum16::Qux]
.choose(&mut rng)
.unwrap()
.to_owned(),
// See also: https://clickhouse.com/docs/en/sql-reference/data-types/decimal
decimal32_9_4: Decimal32::from_str("99999.9999").unwrap(),
decimal64_18_8: Decimal64::from_str("9999999999.99999999").unwrap(),
decimal128_38_12: Decimal128::from_str("99999999999999999999999999.999999999999")
.unwrap(),
// Allowed values ranges:
// - Date = [1970-01-01, 2149-06-06]
// - Date32 = [1900-01-01, 2299-12-31]
// See
// - https://clickhouse.com/docs/en/sql-reference/data-types/date
// - https://clickhouse.com/docs/en/sql-reference/data-types/date32
date: Date::from_calendar_date(2149, Month::June, 6).unwrap(),
date32: Date::from_calendar_date(2299, Month::December, 31).unwrap(),
datetime: max_datetime(),
datetime_tz: max_datetime(),
datetime64_0: max_datetime64(),
datetime64_3: max_datetime64(),
datetime64_6: max_datetime64(),
datetime64_9: max_datetime64_nanos(),
datetime64_9_tz: max_datetime64_nanos(),
}
}
}

impl Default for Row {
fn default() -> Self {
Self::new()
}
}

fn random_str() -> String {
rand::thread_rng()
.sample_iter(&Alphanumeric)
.take(3)
.map(char::from)
.collect()
}

fn max_datetime() -> OffsetDateTime {
OffsetDateTime::from_unix_timestamp(u32::MAX.into()).unwrap()
}

// The allowed range for DateTime64(8) and lower is
// [1900-01-01 00:00:00, 2299-12-31 23:59:59.99999999] UTC
// See https://clickhouse.com/docs/en/sql-reference/data-types/datetime64
fn max_datetime64() -> OffsetDateTime {
// 2262-04-11 23:47:16
OffsetDateTime::new_utc(
Date::from_calendar_date(2299, Month::December, 31).unwrap(),
Time::from_hms_micro(23, 59, 59, 999_999).unwrap(),
)
}

// DateTime64(8)/DateTime(9) allowed range is
// [1900-01-01 00:00:00, 2262-04-11 23:47:16] UTC
// See https://clickhouse.com/docs/en/sql-reference/data-types/datetime64
fn max_datetime64_nanos() -> OffsetDateTime {
OffsetDateTime::new_utc(
Date::from_calendar_date(2262, Month::April, 11).unwrap(),
Time::from_hms_nano(23, 47, 15, 999_999_999).unwrap(),
)
}