Skip to content

Commit 476d846

Browse files
committed
feat: add with_role to Client, Query, Insert{er}
1 parent d2751d3 commit 476d846

File tree

4 files changed

+138
-15
lines changed

4 files changed

+138
-15
lines changed

src/insert.rs

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -95,24 +95,21 @@ impl InsertState {
9595
}
9696
}
9797

98+
fn expect_client_mut(&mut self) -> &mut Client {
99+
let Self::NotStarted { client, .. } = self else {
100+
panic!("cannot modify client options while an insert is in-progress")
101+
};
102+
103+
client
104+
}
105+
98106
fn terminated(&mut self) {
99107
replace_with_or_abort(self, |_self| match _self {
100108
InsertState::NotStarted { .. } => InsertState::Completed, // empty insert
101109
InsertState::Active { handle, .. } => InsertState::Terminated { handle },
102110
_ => unreachable!(),
103111
});
104112
}
105-
106-
fn with_option(&mut self, name: impl Into<String>, value: impl Into<String>) {
107-
assert!(matches!(self, InsertState::NotStarted { .. }));
108-
replace_with_or_abort(self, |_self| match _self {
109-
InsertState::NotStarted { mut client, sql } => {
110-
client.add_option(name, value);
111-
InsertState::NotStarted { client, sql }
112-
}
113-
_ => unreachable!(),
114-
});
115-
}
116113
}
117114

118115
// It should be a regular function, but it decreases performance.
@@ -185,14 +182,40 @@ impl<T> Insert<T> {
185182
self
186183
}
187184

185+
/// Set the [role] to use when executing `INSERT` statements.
186+
///
187+
/// Overrides any role previously set by [`Client::with_role()`] or [`Client::with_option()`].
188+
///
189+
/// [role]: https://clickhouse.com/docs/operations/access-rights#role-management
190+
///
191+
/// # Panics
192+
/// If called after the request is started, e.g., after [`Insert::write`].
193+
pub fn with_role(mut self, role: impl Into<String>) -> Self {
194+
self.state.expect_client_mut().set_role(Some(role.into()));
195+
self
196+
}
197+
198+
/// Perform inserts without any explicit [role] set.
199+
///
200+
/// Overrides any role previously set by [`Client::with_role()`] or [`Client::with_option()`].
201+
///
202+
/// [role]: https://clickhouse.com/docs/operations/access-rights#role-management
203+
///
204+
/// # Panics
205+
/// If called after the request is started, e.g., after [`Insert::write`].
206+
pub fn with_default_role(mut self) -> Self {
207+
self.state.expect_client_mut().set_role(None);
208+
self
209+
}
210+
188211
/// Similar to [`Client::with_option`], but for this particular INSERT
189212
/// statement only.
190213
///
191214
/// # Panics
192215
/// If called after the request is started, e.g., after [`Insert::write`].
193216
#[track_caller]
194217
pub fn with_option(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
195-
self.state.with_option(name, value);
218+
self.state.expect_client_mut().add_option(name, value);
196219
self
197220
}
198221

src/inserter.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,46 @@ where
161161
self
162162
}
163163

164+
/// Set the [role] to use when executing `INSERT` statements.
165+
///
166+
/// Overrides any role previously set by [`Client::with_role()`] or [`Client::with_option()`].
167+
///
168+
/// This does not take effect until the next `INSERT` statement begins
169+
/// if one is already in-progress.
170+
///
171+
/// If you have already begun writing data, you may call [`.force_commit()`][Self::force_commit]
172+
/// to end the current `INSERT` so this takes effect on the next call to [`.write()`][Self::write].
173+
///
174+
/// [role]: https://clickhouse.com/docs/operations/access-rights#role-management
175+
pub fn with_role(mut self, role: impl Into<String>) -> Self {
176+
self.client.set_role(Some(role.into()));
177+
self
178+
}
179+
180+
/// Perform inserts without any explicit [role] set.
181+
///
182+
/// Overrides any role previously set by [`Client::with_role()`] or [`Client::with_option()`].
183+
///
184+
/// This does not take effect until the next `INSERT` statement begins
185+
/// if one is already in-progress.
186+
///
187+
/// If you have already begun writing data, you may call [`.force_commit()`][Self::force_commit]
188+
/// to end the current `INSERT` so this takes effect on the next call to [`.write()`][Self::write].
189+
///
190+
/// [role]: https://clickhouse.com/docs/operations/access-rights#role-management
191+
pub fn with_default_role(mut self) -> Self {
192+
self.client.set_role(None);
193+
self
194+
}
195+
164196
/// Similar to [`Client::with_option`], but for the INSERT statements
165197
/// generated by this [`Inserter`] only.
198+
///
199+
/// This does not take effect until the next `INSERT` statement begins
200+
/// if one is already in-progress.
201+
///
202+
/// If you have already begun writing data, you may call [`.force_commit()`][Self::force_commit]
203+
/// to end the current `INSERT` so this takes effect on the next call to [`.write()`][Self::write].
166204
pub fn with_option(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
167205
self.client.add_option(name, value);
168206
self

src/lib.rs

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub use clickhouse_macros::Row;
1616
use clickhouse_types::{Column, DataTypeNode};
1717

1818
use crate::_priv::row_insert_metadata_query;
19+
use std::borrow::Cow;
1920
use std::{collections::HashMap, fmt::Display, sync::Arc};
2021
use tokio::sync::RwLock;
2122

@@ -57,7 +58,7 @@ pub struct Client {
5758
database: Option<String>,
5859
authentication: Authentication,
5960
compression: Compression,
60-
options: HashMap<String, String>,
61+
options: HashMap<Cow<'static, str>, String>,
6162
headers: HashMap<String, String>,
6263
products_info: Vec<ProductInfo>,
6364
validation: bool,
@@ -227,6 +228,30 @@ impl Client {
227228
self
228229
}
229230

231+
/// Set the [role] to use when executing statements with this `Client` instance.
232+
///
233+
/// Overrides any role previously set by [`Client::with_role()`] or [`Client::with_option()`].
234+
///
235+
/// This setting is copied into cloned clients.
236+
///
237+
/// [role]: https://clickhouse.com/docs/operations/access-rights#role-management
238+
pub fn with_role(mut self, role: impl Into<String>) -> Self {
239+
self.set_role(Some(role.into()));
240+
self
241+
}
242+
243+
/// Execute subsequent statements with this `Client` instance without any explicit [role] set.
244+
///
245+
/// Overrides any role previously set by [`Client::with_role()`] or [`Client::with_option()`].
246+
///
247+
/// This setting is copied into cloned clients.
248+
///
249+
/// [role]: https://clickhouse.com/docs/operations/access-rights#role-management
250+
pub fn with_default_role(mut self) -> Self {
251+
self.set_role(None);
252+
self
253+
}
254+
230255
/// A JWT access token to authenticate with ClickHouse.
231256
/// JWT token authentication is supported in ClickHouse Cloud only.
232257
/// Should not be called after [`Client::with_user`] or
@@ -278,7 +303,7 @@ impl Client {
278303
/// Client::default().with_option("allow_nondeterministic_mutations", "1");
279304
/// ```
280305
pub fn with_option(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
281-
self.options.insert(name.into(), value.into());
306+
self.options.insert(Cow::Owned(name.into()), value.into());
282307
self
283308
}
284309

@@ -447,7 +472,22 @@ impl Client {
447472
/// Used internally to modify the options map of an _already cloned_
448473
/// [`Client`] instance.
449474
pub(crate) fn add_option(&mut self, name: impl Into<String>, value: impl Into<String>) {
450-
self.options.insert(name.into(), value.into());
475+
self.options.insert(Cow::Owned(name.into()), value.into());
476+
}
477+
478+
pub(crate) fn set_role(&mut self, role: Option<String>) {
479+
// By setting the role via `options`, we can be sure we've overwritten any role
480+
// manually set by the user with `with_option()`.
481+
let key = Cow::Borrowed("role");
482+
483+
match role {
484+
Some(role) => {
485+
self.options.insert(key, role);
486+
}
487+
None => {
488+
self.options.remove(&key);
489+
}
490+
}
451491
}
452492

453493
/// Use a mock server for testing purposes.

src/query.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,28 @@ impl Query {
3939
&self.sql
4040
}
4141

42+
/// Perform this query with the given [role] set.
43+
///
44+
/// Overrides any role previously set
45+
///
46+
/// [role]: https://clickhouse.com/docs/operations/access-rights#role-management
47+
pub fn with_role(self, role: impl Into<String>) -> Self {
48+
Self {
49+
client: self.client.with_role(role),
50+
..self
51+
}
52+
}
53+
54+
/// Perform this query without any explicit [role] set.
55+
///
56+
/// [role]: https://clickhouse.com/docs/operations/access-rights#role-management
57+
pub fn with_default_role(self) -> Self {
58+
Self {
59+
client: self.client.with_default_role(),
60+
..self
61+
}
62+
}
63+
4264
/// Binds `value` to the next `?` in the query.
4365
///
4466
/// The `value`, which must either implement [`Serialize`] or be an

0 commit comments

Comments
 (0)