chdb_rust/query_result.rs
1//! Query result handling for chDB.
2//!
3//! This module provides the [`QueryResult`] type for accessing query execution results.
4
5use core::slice;
6use std::borrow::Cow;
7use std::ffi::CStr;
8use std::time::Duration;
9
10use crate::bindings;
11use crate::error::Error;
12use crate::error::Result;
13
14/// The result of a query execution.
15///
16/// `QueryResult` contains the output data from a query execution, along with
17/// metadata such as execution time and number of rows read.
18///
19/// # Thread Safety
20///
21/// `QueryResult` implements `Send`, meaning it can be safely transferred between threads.
22///
23/// # Examples
24///
25/// ```no_run
26/// use chdb_rust::execute;
27/// use chdb_rust::format::OutputFormat;
28/// use chdb_rust::arg::Arg;
29///
30/// let result = execute(
31/// "SELECT number FROM numbers(10)",
32/// Some(&[Arg::OutputFormat(OutputFormat::JSONEachRow)])
33/// )?;
34///
35/// // Access the data as a string
36/// println!("Data: {}", result.data_utf8_lossy());
37///
38/// // Access metadata
39/// println!("Rows read: {}", result.rows_read());
40/// println!("Bytes read: {}", result.bytes_read());
41/// println!("Elapsed time: {:?}", result.elapsed());
42/// # Ok::<(), chdb_rust::error::Error>(())
43/// ```
44#[derive(Debug)]
45pub struct QueryResult {
46 inner: *mut bindings::chdb_result,
47}
48
49// Safety: QueryResult is safe to send between threads
50// The underlying chDB result structure is thread-safe for read access
51unsafe impl Send for QueryResult {}
52
53impl QueryResult {
54 pub(crate) fn new(inner: *mut bindings::chdb_result) -> Self {
55 Self { inner }
56 }
57
58 /// Get the result data as a UTF-8 string.
59 ///
60 /// This method validates that the data is valid UTF-8. If the data contains
61 /// invalid UTF-8 sequences, it returns an error.
62 ///
63 /// # Returns
64 ///
65 /// Returns a `String` containing the query result, or an error if the data
66 /// contains invalid UTF-8 sequences.
67 ///
68 /// # Examples
69 ///
70 /// ```no_run
71 /// use chdb_rust::execute;
72 ///
73 /// let result = execute("SELECT 'Hello, World!' AS greeting", None)?;
74 /// let data = result.data_utf8()?;
75 /// println!("{}", data);
76 /// # Ok::<(), chdb_rust::error::Error>(())
77 /// ```
78 ///
79 /// # Errors
80 ///
81 /// Returns [`Error::NonUtf8Sequence`] if the
82 /// result data contains invalid UTF-8 sequences. Use [`data_utf8_lossy`](Self::data_utf8_lossy)
83 /// if you want to handle invalid UTF-8 gracefully.
84 pub fn data_utf8(&self) -> Result<String> {
85 let buf = self.data_ref();
86 String::from_utf8(buf.to_vec()).map_err(Error::NonUtf8Sequence)
87 }
88
89 /// Get the result data as a UTF-8 string, replacing invalid sequences.
90 ///
91 /// This method converts the result data to a string, replacing any invalid UTF-8
92 /// sequences with the Unicode replacement character (U+FFFD).
93 ///
94 /// # Returns
95 ///
96 /// Returns a `Cow<str>` containing the query result. Invalid UTF-8 sequences
97 /// are replaced with the replacement character.
98 ///
99 /// # Examples
100 ///
101 /// ```no_run
102 /// use chdb_rust::execute;
103 ///
104 /// let result = execute("SELECT 'Hello, World!' AS greeting", None)?;
105 /// let data = result.data_utf8_lossy();
106 /// println!("{}", data);
107 /// # Ok::<(), chdb_rust::error::Error>(())
108 /// ```
109 pub fn data_utf8_lossy(&self) -> Cow<'_, str> {
110 String::from_utf8_lossy(self.data_ref())
111 }
112
113 /// Get the result data as a UTF-8 string without validation.
114 ///
115 /// # Safety
116 ///
117 /// This function is marked as safe, but it will produce invalid UTF-8 strings
118 /// if the underlying data contains non-UTF-8 bytes. Only use this if you're
119 /// certain the data is valid UTF-8, or if you're prepared to handle potentially
120 /// invalid strings.
121 ///
122 /// # Examples
123 ///
124 /// ```no_run
125 /// use chdb_rust::execute;
126 ///
127 /// let result = execute("SELECT 'Hello' AS greeting", None)?;
128 /// let data = result.data_utf8_unchecked();
129 /// println!("{}", data);
130 /// # Ok::<(), chdb_rust::error::Error>(())
131 /// ```
132 pub fn data_utf8_unchecked(&self) -> String {
133 unsafe { String::from_utf8_unchecked(self.data_ref().to_vec()) }
134 }
135
136 /// Get a reference to the raw result data as bytes.
137 ///
138 /// This method returns a byte slice containing the raw query result data.
139 /// The data is in the format specified when executing the query (e.g., JSON, CSV, etc.).
140 ///
141 /// # Returns
142 ///
143 /// Returns a byte slice containing the query result data. Returns an empty slice
144 /// if there's no data or if the buffer pointer is null.
145 ///
146 /// # Examples
147 ///
148 /// ```no_run
149 /// use chdb_rust::execute;
150 ///
151 /// let result = execute("SELECT 1 AS value", None)?;
152 /// let bytes = result.data_ref();
153 /// println!("Data length: {} bytes", bytes.len());
154 /// # Ok::<(), chdb_rust::error::Error>(())
155 /// ```
156 pub fn data_ref(&self) -> &[u8] {
157 let buf = unsafe { bindings::chdb_result_buffer(self.inner) };
158 let len = unsafe { bindings::chdb_result_length(self.inner) };
159 if buf.is_null() || len == 0 {
160 return &[];
161 }
162 unsafe { slice::from_raw_parts(buf as *const u8, len) }
163 }
164
165 /// Get the number of rows read by the query.
166 ///
167 /// This returns the total number of rows that were read from storage during
168 /// query execution.
169 ///
170 /// # Returns
171 ///
172 /// Returns the number of rows read as a `u64`.
173 ///
174 /// # Examples
175 ///
176 /// ```no_run
177 /// use chdb_rust::execute;
178 ///
179 /// let result = execute("SELECT number FROM numbers(100)", None)?;
180 /// println!("Rows read: {}", result.rows_read());
181 /// # Ok::<(), chdb_rust::error::Error>(())
182 /// ```
183 pub fn rows_read(&self) -> u64 {
184 unsafe { bindings::chdb_result_rows_read(self.inner) }
185 }
186
187 /// Get the number of bytes read by the query.
188 ///
189 /// This returns the total number of bytes that were read from storage during
190 /// query execution.
191 ///
192 /// # Returns
193 ///
194 /// Returns the number of bytes read as a `u64`.
195 ///
196 /// # Examples
197 ///
198 /// ```no_run
199 /// use chdb_rust::execute;
200 ///
201 /// let result = execute("SELECT number FROM numbers(100)", None)?;
202 /// println!("Bytes read: {}", result.bytes_read());
203 /// # Ok::<(), chdb_rust::error::Error>(())
204 /// ```
205 pub fn bytes_read(&self) -> u64 {
206 unsafe { bindings::chdb_result_bytes_read(self.inner) }
207 }
208
209 /// Get the elapsed time for query execution.
210 ///
211 /// This returns the time it took to execute the query, measured from when
212 /// the query was submitted until the result was ready.
213 ///
214 /// # Returns
215 ///
216 /// Returns a [`Duration`] representing the elapsed time.
217 ///
218 /// # Examples
219 ///
220 /// ```no_run
221 /// use chdb_rust::execute;
222 ///
223 /// let result = execute("SELECT number FROM numbers(1000)", None)?;
224 /// println!("Query took: {:?}", result.elapsed());
225 /// # Ok::<(), chdb_rust::error::Error>(())
226 /// ```
227 pub fn elapsed(&self) -> Duration {
228 let elapsed = unsafe { bindings::chdb_result_elapsed(self.inner) };
229 Duration::from_secs_f64(elapsed)
230 }
231
232 pub(crate) fn check_error(self) -> Result<Self> {
233 self.check_error_ref()?;
234 Ok(self)
235 }
236
237 pub(crate) fn check_error_ref(&self) -> Result<()> {
238 let err_ptr = unsafe { bindings::chdb_result_error(self.inner) };
239
240 if err_ptr.is_null() {
241 return Ok(());
242 }
243
244 let err_msg = unsafe { CStr::from_ptr(err_ptr).to_string_lossy().to_string() };
245 if err_msg.is_empty() {
246 return Ok(());
247 }
248
249 Err(Error::QueryError(err_msg))
250 }
251}
252
253impl Drop for QueryResult {
254 fn drop(&mut self) {
255 unsafe { bindings::chdb_destroy_query_result(self.inner) };
256 }
257}