-
Notifications
You must be signed in to change notification settings - Fork 134
feat: add QualifiedIdentifier type #287
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
base: main
Are you sure you want to change the base?
feat: add QualifiedIdentifier type #287
Conversation
I am hesitant about expanding on the client-side binding, which is included in this client for historical reasons. Ideally, you should use server-side params: SET param_db = 'system';
SET param_tbl = 'numbers';
SET param_col = 'number';
SET param_val = '3';
SELECT {tbl:Identifier}.{col:Identifier}
FROM {db:Identifier}.{tbl:Identifier}
WHERE {col:Identifier} < {val:UInt64}
┌─number─┐
1. │ 0 │
2. │ 1 │
3. │ 2 │
└────────┘ Curl equivalent: curl -XPOST "http://localhost:8123?param_db=system¶m_tbl=numbers¶m_col=number¶m_val=3" --data-binary "SELECT {tbl:Identifier}.{col:Identifier} FROM {db:Identifier}.{tbl:Identifier} WHERE {col:Identifier} < {val:UInt64} FORMAT Pretty"
┏━━━━━━━━┓
┃ number ┃
┡━━━━━━━━┩
1. │ 0 │
├────────┤
2. │ 1 │
├────────┤
3. │ 2 │
└────────┘ And, finally, Rust equivalent: use clickhouse::{error::Result, Client};
#[tokio::main]
async fn main() -> Result<()> {
let client = Client::default().with_url("http://localhost:8123");
let result = client
.query(
"
SELECT {tbl:Identifier}.{col:Identifier}
FROM {db:Identifier}.{tbl:Identifier}
WHERE {col:Identifier} < {val:UInt64}
",
)
.param("db", "system")
.param("tbl", "numbers")
.param("col", "number")
.param("val", 3u64)
.fetch_all::<u64>()
.await?;
println!("Parametrized query output: {:?}", result);
Ok(())
} Which prints:
|
I've added an example for server-side params here: #288. |
We have some very large queries; I'm worried about how well clickhouse will do when it's binding potentially hundreds of server-side params. Those would be some big http requests! |
That is a valid concern since query parameters are passed in the URL: https://clickhouse.com/docs/interfaces/http#cli-queries-with-parameters The maximum length of a request URI appears to be governed by However, what I would be more concerned about is if ClickHouse is behind a cloud load balancer. Both GCP and AWS have much tighter limits on the size of request headers they will accept:
It doesn't appear to be documented, but it looks like it might be actually be possible to submit a query as a I'm not sure if this is designed for general use, however. The comment here just mentions "external data for query processing" https://github.com/ClickHouse/ClickHouse/blob/8adba6a235b093f7b468b7d9a936c4e02330d280/src/Server/HTTPHandler.cpp#L820-L821 |
@svix-jbrown can you elaborate a little about your use of query parameters? Are you using them to insert data, or do you have, e.g., |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we're prepared to accept this since we maybe have some limitations with server-side params that we need to figure out.
Just a couple nits.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good now, just needs a rebase and the merge conflict in CHANGELOG.md
fixed.
I double-checked the current implementation. let data = client
.query("SELECT number FROM ?.?")
.bind(Identifier("system")) // db
.bind(Identifier("numbers")) // table
.with_option("limit", "3")
.fetch_all::<u64>()
.await?; Perhaps it can work in your use case, and we don't need to bloat the client-side binding further? |
One downside with that approach is it's perhaps not the most obvious solution. I think if that's the route we end up recommending, we should add an example to One benefit of let mut query = client.query("SELECT foo FROM ?");
query = if use_external_bar {
query.bind(QualifiedIdentifier("external_db", "bar"))
} else {
query.bind(Identifier("bar"))
}; |
impl Bind for QualifiedIdentifier<'_> { | ||
#[inline] | ||
fn write(&self, dst: &mut impl fmt::Write) -> Result<(), String> { | ||
if self.0.len() > 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should fix the Clippy error:
if self.0.len() > 0 { | |
if !self.0.is_empty() { |
Summary
Fixes #286
Checklist