clickhouse/query_summary.rs
1use std::collections::HashMap;
2
3/// Parsed representation of the `X-ClickHouse-Summary` HTTP response header.
4///
5/// Provides typed getters for known fields and a generic [`get`](Self::get)
6/// fallback for forward-compatibility with future ClickHouse versions.
7///
8/// All getters return `Option<u64>`: `None` if the field is absent or cannot
9/// be parsed. This ensures deserialization never fails, even if ClickHouse
10/// renames, removes, or adds fields.
11///
12/// Note: the summary values may be incomplete unless the query was executed
13/// with `wait_end_of_query=1`, because ClickHouse sends this header before
14/// the response body and the values reflect progress at that point.
15#[derive(Debug, Clone)]
16pub struct QuerySummary {
17 fields: HashMap<String, String>,
18}
19
20impl QuerySummary {
21 /// Returns the raw string value for the given key, if present.
22 ///
23 /// Use this to access fields that are not yet covered by typed getters,
24 /// e.g. fields added in newer ClickHouse versions.
25 pub fn get(&self, key: &str) -> Option<&str> {
26 self.fields.get(key).map(String::as_str)
27 }
28
29 pub fn read_rows(&self) -> Option<u64> {
30 self.get_u64("read_rows")
31 }
32
33 pub fn read_bytes(&self) -> Option<u64> {
34 self.get_u64("read_bytes")
35 }
36
37 pub fn written_rows(&self) -> Option<u64> {
38 self.get_u64("written_rows")
39 }
40
41 pub fn written_bytes(&self) -> Option<u64> {
42 self.get_u64("written_bytes")
43 }
44
45 pub fn total_rows_to_read(&self) -> Option<u64> {
46 self.get_u64("total_rows_to_read")
47 }
48
49 pub fn result_rows(&self) -> Option<u64> {
50 self.get_u64("result_rows")
51 }
52
53 pub fn result_bytes(&self) -> Option<u64> {
54 self.get_u64("result_bytes")
55 }
56
57 pub fn elapsed_ns(&self) -> Option<u64> {
58 self.get_u64("elapsed_ns")
59 }
60
61 pub fn memory_usage(&self) -> Option<u64> {
62 self.get_u64("memory_usage")
63 }
64
65 fn get_u64(&self, key: &str) -> Option<u64> {
66 self.fields.get(key)?.parse().ok()
67 }
68
69 /// Parses the raw header value into a `QuerySummary`.
70 ///
71 /// Returns `None` if the value is not valid JSON or not an object with
72 /// string values. This matches ClickHouse's encoding, where all values
73 /// are JSON strings (e.g. `"1000"` instead of `1000`).
74 pub(crate) fn from_header(raw: &str) -> Option<Self> {
75 let fields: HashMap<String, String> = serde_json::from_str(raw).ok()?;
76 Some(Self { fields })
77 }
78}