Skip to content

Commit c9636c3

Browse files
committed
feat: add ScopedIdentifier type
1 parent 56c5dd3 commit c9636c3

File tree

3 files changed

+38
-2
lines changed

3 files changed

+38
-2
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2525
- query: due to `RowBinaryWithNamesAndTypes` format usage, there might be an impact on fetch performance, which largely
2626
depends on how the dataset is defined. If you notice decreased performance, consider disabling validation by using
2727
`Client::with_validation(false)`.
28-
- serde: it is now possible to deserialize Map ClickHouse type into `HashMap<K, V>` (or `BTreeMap`, `IndexMap`,
28+
- serde: it is now possible to deserialize Map ClickHouse type into `HashMap<K, V>` (or `BTreeMap`, `IndexMap`,
2929
`DashMap`, etc.).
3030

3131
### Added
@@ -36,6 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3636
- types: a new crate `clickhouse-types` was added to the project workspace. This crate is required for
3737
`RowBinaryWithNamesAndTypes` struct definition validation, as it contains ClickHouse data types AST, as well as
3838
functions and utilities to parse the types out of the ClickHouse server response. ([#221]).
39+
- sql: added `ScopedIdentifier` type to represent scoped identifiers (e.g., `table.column`) safely as bind values
3940

4041
[#221]: https://github.com/ClickHouse/clickhouse-rs/pull/221
4142
[#245]: https://github.com/ClickHouse/clickhouse-rs/pull/245

src/sql/bind.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,38 @@ impl Bind for Identifier<'_> {
3131
escape::identifier(self.0, dst).map_err(|err| err.to_string())
3232
}
3333
}
34+
35+
#[derive(Clone, Copy)]
36+
pub struct ScopedIdentifier<'a>(pub &'a str, pub &'a str);
37+
38+
#[sealed]
39+
impl Bind for ScopedIdentifier<'_> {
40+
#[inline]
41+
fn write(&self, dst: &mut impl fmt::Write) -> Result<(), String> {
42+
if self.0.len() > 0 {
43+
escape::identifier(self.0, dst).map_err(|err| err.to_string())?;
44+
dst.write_char('.').map_err(|err| err.to_string())?;
45+
}
46+
escape::identifier(self.1, dst).map_err(|err| err.to_string())
47+
}
48+
}
49+
50+
#[cfg(test)]
51+
mod tests {
52+
use super::{Bind, ScopedIdentifier};
53+
54+
fn bind_to_string(b: impl Bind) -> String {
55+
let mut s = String::new();
56+
b.write(&mut s).expect("bind should succeed");
57+
s
58+
}
59+
60+
#[test]
61+
fn test_scoped_identifier() {
62+
assert_eq!(
63+
bind_to_string(ScopedIdentifier("foo", "bar baz")),
64+
"`foo`.`bar baz`"
65+
);
66+
assert_eq!(bind_to_string(ScopedIdentifier("", "bar baz")), "`bar baz`");
67+
}
68+
}

src/sql/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::{
55
row::{self, Row},
66
};
77

8-
pub use bind::{Bind, Identifier};
8+
pub use bind::{Bind, Identifier, ScopedIdentifier};
99

1010
mod bind;
1111
pub(crate) mod escape;

0 commit comments

Comments
 (0)