Skip to content

Commit 63f3f2e

Browse files
authored
feat: support byte-string params, implement Primitive for bytes::Bytes{Mut} (#376)
1 parent 8c1ff94 commit 63f3f2e

File tree

5 files changed

+39
-3
lines changed

5 files changed

+39
-3
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ clickhouse-types = { version = "0.1.1", path = "types" }
127127

128128
thiserror = "2.0"
129129
serde = "1.0.106"
130-
bytes = "1.5.0"
130+
bytes = { version = "1.5.0", features = ["serde"] }
131131
tokio = { version = "1.0.1", features = ["rt", "macros"] }
132132
http-body-util = "0.1.2"
133133
hyper = "1.4"

src/row.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,24 @@ macro_rules! impl_primitive_for {
204204

205205
// TODO: char? &str? SocketAddr? Path? Duration? NonZero*?
206206
impl_primitive_for![
207-
bool, String, u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f32, f64,
207+
bool,
208+
String,
209+
u8,
210+
u16,
211+
u32,
212+
u64,
213+
u128,
214+
usize,
215+
i8,
216+
i16,
217+
i32,
218+
i64,
219+
i128,
220+
isize,
221+
f32,
222+
f64,
223+
bytes::Bytes,
224+
bytes::BytesMut,
208225
];
209226

210227
macro_rules! count_tokens {

src/sql/escape.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ pub(crate) fn escape(src: &str, dst: &mut impl fmt::Write) -> fmt::Result {
2929
dst.write_str(rest)
3030
}
3131

32+
pub(crate) fn escape_ascii(s: &[u8], dst: &mut impl fmt::Write) -> fmt::Result {
33+
write!(dst, "{}", s.escape_ascii())
34+
}
35+
3236
// See https://clickhouse.com/docs/en/sql-reference/syntax#string
3337
pub(crate) fn hex_bytes(s: &[u8], dst: &mut impl fmt::Write) -> fmt::Result {
3438
dst.write_char('X')?;

src/sql/ser.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,6 @@ impl<'a, W: Write> Serializer for ParamSerializer<'a, W> {
358358

359359
unsupported!(
360360
serialize_map(Option<usize>) -> Result<Impossible>,
361-
serialize_bytes(&[u8]),
362361
serialize_unit,
363362
serialize_unit_struct(&'static str),
364363
);
@@ -379,6 +378,11 @@ impl<'a, W: Write> Serializer for ParamSerializer<'a, W> {
379378
serialize_bool(bool),
380379
);
381380

381+
#[inline]
382+
fn serialize_bytes(self, v: &[u8]) -> std::result::Result<Self::Ok, Self::Error> {
383+
Ok(escape::escape_ascii(v, self.writer)?)
384+
}
385+
382386
#[inline]
383387
fn serialize_char(self, value: char) -> Result {
384388
let mut tmp = [0u8; 4];

tests/it/query.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use bytes::Bytes;
12
use serde::{Deserialize, Serialize};
23

34
use clickhouse::sql::Identifier;
@@ -122,6 +123,16 @@ async fn server_side_param() {
122123
.await
123124
.expect("failed to fetch string");
124125
assert_eq!(result, &["a", "bc"]);
126+
127+
// Should not be valid UTF-8
128+
let bytes = Bytes::from_static(b"Hello, world!\\\0\t\r\n\x00\x01\xFF\xFE\xFD");
129+
let result = client
130+
.query("SELECT {val1: String} AS result")
131+
.param("val1", &bytes)
132+
.fetch_one::<Bytes>()
133+
.await
134+
.expect("failed to fetch bytes");
135+
assert_eq!(result, bytes);
125136
}
126137

127138
// See #19.

0 commit comments

Comments
 (0)