Skip to content

Commit 99a24a1

Browse files
fix(ser): fix i128/u128 SQL bind serialization
Integer types larger than 64 bits cannot be written as literals without casting, or they end up `Float64`. See ClickHouse/ClickHouse#38480 This commit fixes #208
1 parent 72b3729 commit 99a24a1

File tree

3 files changed

+137
-3
lines changed

3 files changed

+137
-3
lines changed

src/sql/ser.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,10 @@ impl<'a, W: Write> Serializer for SqlSerializer<'a, W> {
9595
serialize_i16(i16),
9696
serialize_i32(i32),
9797
serialize_i64(i64),
98-
serialize_i128(i128),
9998
serialize_u8(u8),
10099
serialize_u16(u16),
101100
serialize_u32(u32),
102101
serialize_u64(u64),
103-
serialize_u128(u128),
104102
serialize_f32(f32),
105103
serialize_f64(f64),
106104
serialize_bool(bool),
@@ -112,6 +110,18 @@ impl<'a, W: Write> Serializer for SqlSerializer<'a, W> {
112110
self.serialize_str(value.encode_utf8(&mut tmp))
113111
}
114112

113+
#[inline]
114+
fn serialize_i128(self, value: i128) -> Result {
115+
write!(self.writer, "{}::Int128", value)?;
116+
Ok(())
117+
}
118+
119+
#[inline]
120+
fn serialize_u128(self, value: u128) -> Result {
121+
write!(self.writer, "{}::UInt128", value)?;
122+
Ok(())
123+
}
124+
115125
#[inline]
116126
fn serialize_str(self, value: &str) -> Result {
117127
escape::string(value, self.writer)?;
@@ -452,7 +462,8 @@ mod tests {
452462
fn it_writes_numeric_primitives() {
453463
assert_eq!(check(42), "42");
454464
assert_eq!(check(42.5), "42.5");
455-
assert_eq!(check(42u128), "42");
465+
assert_eq!(check(42u128), "42::UInt128");
466+
assert_eq!(check(42i128), "42::Int128");
456467
}
457468

458469
#[test]

tests/it/int128.rs

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
use rand::random;
2+
use serde::{Deserialize, Serialize};
3+
4+
use clickhouse::Row;
5+
6+
#[tokio::test]
7+
async fn u128() {
8+
let client = prepare_database!();
9+
10+
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Row)]
11+
struct MyRow {
12+
id: u128,
13+
value: String,
14+
}
15+
16+
client
17+
.query(
18+
"
19+
CREATE TABLE test(
20+
id UInt128,
21+
value String,
22+
) ENGINE = MergeTree ORDER BY id
23+
",
24+
)
25+
.execute()
26+
.await
27+
.unwrap();
28+
29+
let (id0, id1, id2) = (random(), random(), random());
30+
println!("ids: {id0}, {id1}, {id2}");
31+
32+
let original_rows = vec![
33+
MyRow {
34+
id: id0,
35+
value: "test_0".to_string(),
36+
},
37+
MyRow {
38+
id: id1,
39+
value: "test_1".to_string(),
40+
},
41+
MyRow {
42+
id: id2,
43+
value: "test_2".to_string(),
44+
},
45+
];
46+
47+
let mut insert = client.insert("test").unwrap();
48+
for row in &original_rows {
49+
insert.write(row).await.unwrap();
50+
}
51+
insert.end().await.unwrap();
52+
53+
let rows = client
54+
.query("SELECT ?fields FROM test WHERE id IN ? ORDER BY value")
55+
.bind(vec![id0, id2])
56+
.fetch_all::<MyRow>()
57+
.await
58+
.unwrap();
59+
60+
assert_eq!(rows.len(), 2);
61+
assert_eq!(rows[0], original_rows[0]);
62+
assert_eq!(rows[1], original_rows[2]);
63+
}
64+
65+
#[tokio::test]
66+
async fn i128() {
67+
let client = prepare_database!();
68+
69+
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Row)]
70+
struct MyRow {
71+
id: i128,
72+
value: String,
73+
}
74+
75+
client
76+
.query(
77+
"
78+
CREATE TABLE test(
79+
id Int128,
80+
value String,
81+
) ENGINE = MergeTree ORDER BY id
82+
",
83+
)
84+
.execute()
85+
.await
86+
.unwrap();
87+
88+
let (id0, id1, id2) = (random(), random(), random());
89+
println!("ids: {id0}, {id1}, {id2}");
90+
91+
let original_rows = vec![
92+
MyRow {
93+
id: id0,
94+
value: "test_0".to_string(),
95+
},
96+
MyRow {
97+
id: id1,
98+
value: "test_1".to_string(),
99+
},
100+
MyRow {
101+
id: id2,
102+
value: "test_2".to_string(),
103+
},
104+
];
105+
106+
let mut insert = client.insert("test").unwrap();
107+
for row in &original_rows {
108+
insert.write(row).await.unwrap();
109+
}
110+
insert.end().await.unwrap();
111+
112+
let rows = client
113+
.query("SELECT ?fields FROM test WHERE id IN ? ORDER BY value")
114+
.bind(vec![id0, id2])
115+
.fetch_all::<MyRow>()
116+
.await
117+
.unwrap();
118+
119+
assert_eq!(rows.len(), 2);
120+
assert_eq!(rows[0], original_rows[0]);
121+
assert_eq!(rows[1], original_rows[2]);
122+
}

tests/it/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ mod cursor_stats;
6161
mod fetch_bytes;
6262
mod insert;
6363
mod inserter;
64+
mod int128;
6465
mod ip;
6566
mod mock;
6667
mod nested;

0 commit comments

Comments
 (0)