hdbconnect_impl/a_sync/result_set.rs
1use crate::{
2 HdbResult, HdbValue, Row, Rows,
3 base::{RsState, XMutexed},
4 protocol::{ServerUsage, parts::ResultSetMetadata},
5};
6use serde_db::de::DeserializableResultSet;
7use std::sync::Arc;
8
9/// The result of a database query.
10///
11/// This behaves essentially like a set of `Row`s, and each `Row` is a set of `HdbValue`s.
12///
13/// In fact, result sets with a larger number of rows are not returned from the database
14/// in a single roundtrip. `ResultSet` automatically fetches outstanding data
15/// as soon as they are required.
16///
17/// The method [`try_into`](#method.try_into) converts the data from this generic format
18/// in a singe step into your application specific format.
19///
20/// Due to the chunk-wise data transfer, which has to happen asynchronously,
21/// `ResultSet` cannot implement the synchronous trait `std::iter::Iterator`.
22/// Use method [`next_row()`](#method.next_row) as a replacement.
23///
24/// ```
25///
26// (see also <https://rust-lang.github.io/rfcs/2996-async-iterator.html>)
27#[derive(Debug)]
28pub struct ResultSet {
29 metadata: Arc<ResultSetMetadata>,
30 state: Arc<XMutexed<RsState>>,
31}
32
33impl ResultSet {
34 pub(crate) fn new(a_rsmd: Arc<ResultSetMetadata>, rs_state: RsState) -> Self {
35 Self {
36 metadata: a_rsmd,
37 state: Arc::new(XMutexed::new_async(rs_state)),
38 }
39 }
40
41 /// Conveniently translates the complete result set into a rust type that implements
42 /// `serde::Deserialize` and has an adequate structure.
43 /// The implementation of this method uses
44 /// [`serde_db::de`](https://docs.rs/serde_db/latest/serde_db/de/index.html).
45 ///
46 /// A result set is essentially a two-dimensional structure, given as a list
47 /// of rows, where each row is a list of fields; the name of each field is
48 /// given in the metadata of the result set.
49 ///
50 /// The method supports a variety of target data structures, with the only
51 /// strong limitation that no data loss is supported.
52 ///
53 /// It depends on the dimension of the result set what target data
54 /// structure you can choose for deserialization:
55 ///
56 /// * You can always use a `Vec<line_struct>`, if the elements of
57 /// `line_struct` match the field list of the result set.
58 ///
59 /// * If the result set contains only a single line (e.g. because you
60 /// specified `TOP 1` in your select clause),
61 /// then you can optionally choose to deserialize directly into a plain
62 /// `line_struct`.
63 ///
64 /// * If the result set contains only a single column, then you can
65 /// optionally choose to deserialize directly into a
66 /// `Vec<plain_field>`.
67 ///
68 /// * If the result set contains only a single value (one row with one
69 /// column), then you can optionally choose to deserialize into a
70 /// plain `line_struct`, or a `Vec<plain_field>`, or a `plain_field`.
71 ///
72 /// Also the translation of the individual field values provides flexibility.
73 ///
74 /// * You can e.g. convert values from a nullable column
75 /// into a plain field, provided that no NULL values are given in the
76 /// result set.
77 ///
78 /// * Vice versa, you can use an `Option<plain_field>`, even if the column is
79 /// marked as NOT NULL.
80 ///
81 /// * Similarly, integer types can differ, as long as the concrete values
82 /// can be assigned without loss.
83 ///
84 /// As usual with serde deserialization, you need to specify the type of your target variable
85 /// explicitly, so that `try_into()` can derive the type it needs to instantiate:
86 ///
87 /// ```ignore
88 /// #[derive(Deserialize)]
89 /// struct Entity {
90 /// ...
91 /// }
92 /// let typed_result: Vec<Entity> = result_set.try_into()?;
93 /// ```
94 ///
95 /// # Errors
96 ///
97 /// `HdbError::Deserialization` if the deserialization into the target type is not possible.
98 pub async fn try_into<'de, T>(self) -> HdbResult<T>
99 where
100 T: serde::de::Deserialize<'de>,
101 {
102 trace!("ResultSet::try_into()");
103 Ok(DeserializableResultSet::try_into(self.into_rows().await?)?)
104 }
105
106 /// Fetches all rows and all data of contained LOBs
107 ///
108 /// # Errors
109 ///
110 /// Various errors can occur.
111 pub async fn into_rows(self) -> HdbResult<Rows> {
112 self.state
113 .lock_async()
114 .await
115 .as_rows_async(Arc::clone(&self.metadata))
116 .await
117 }
118
119 /// Converts the result set into a single row.
120 ///
121 /// # Errors
122 ///
123 /// `HdbError::Usage` if the result set contains more than a single row, or is empty.
124 pub async fn into_single_row(self) -> HdbResult<Row> {
125 let mut state = self.state.lock_async().await;
126 state.single_row_async().await
127 }
128
129 /// Converts the result set into a single value.
130 ///
131 /// # Errors
132 ///
133 /// `HdbError::Usage` if the result set contains more than a single value, or is empty.
134 pub async fn into_single_value(self) -> HdbResult<HdbValue<'static>> {
135 let mut state = self.state.lock_async().await;
136 state.single_row_async().await?.into_single_value()
137 }
138
139 /// Access to metadata.
140 ///
141 /// ## Examples
142 ///
143 /// ```rust,ignore
144 /// let rs: ResultSet;
145 /// //...
146 /// // get the precision of the second field
147 /// let prec: i16 = result_set.metadata()[1].precision();
148 /// ```
149 ///
150 /// or
151 ///
152 /// ```rust,ignore
153 /// let rs: ResultSet;
154 /// //...
155 /// for field_metadata in &*rs.metadata() {
156 /// // evaluate metadata of a field
157 /// }
158 /// ```
159 #[must_use]
160 pub fn metadata(&self) -> Arc<ResultSetMetadata> {
161 Arc::clone(&self.metadata)
162 }
163
164 /// Returns the total number of rows in the result set,
165 /// including those that still need to be fetched from the database,
166 /// but excluding those that have already been removed from the result set.
167 ///
168 /// This method can be expensive, and it can fail, since it fetches all yet
169 /// outstanding rows from the database.
170 ///
171 /// # Errors
172 ///
173 /// Several variants of `HdbError` are possible.
174 pub async fn total_number_of_rows(&self) -> HdbResult<usize> {
175 self.state
176 .lock_async()
177 .await
178 .total_number_of_rows_async(&self.metadata)
179 .await
180 }
181
182 /// Removes the next row and returns it, or `Ok(None)` if the `ResultSet` is empty.
183 ///
184 /// Consequently, the `ResultSet` has one row less after the call.
185 /// May need to fetch further rows from the database, which can fail.
186 ///
187 /// ```rust, no_run
188 /// # use hdbconnect::{Connection,ConnectParams,HdbResult};
189 /// # use serde::Deserialize;
190 /// # fn main() -> HdbResult<()> {
191 /// # #[derive(Debug, Deserialize)]
192 /// # struct Entity();
193 /// # let mut connection = Connection::new(ConnectParams::builder().build()?)?;
194 /// # let query_str = "";
195 /// let mut rs = connection.query(query_str).await?;
196 /// while let Some(row) = rs.next_row().await? {
197 /// let entity: Entity = row.try_into()?;
198 /// println!("Got entity: {:?}", entity);
199 /// }
200 /// # Ok(())
201 /// # }
202 /// ```
203 ///
204 /// # Errors
205 ///
206 /// Several variants of `HdbError` are possible.
207 pub async fn next_row(&mut self) -> HdbResult<Option<Row>> {
208 self.state
209 .lock_async()
210 .await
211 .next_row_async(&self.metadata)
212 .await
213 }
214
215 /// Fetches all not yet transported result lines from the server.
216 ///
217 /// Bigger result sets are typically not transported in one roundtrip from the database;
218 /// the number of roundtrips depends on the total number of rows in the result set
219 /// and the configured fetch-size of the connection.
220 ///
221 /// Fetching can fail, e.g. if the network connection is broken.
222 ///
223 /// # Errors
224 ///
225 /// Several variants of `HdbError` are possible.
226 pub async fn fetch_all(&self) -> HdbResult<()> {
227 self.state
228 .lock_async()
229 .await
230 .fetch_all_async(&self.metadata)
231 .await
232 }
233
234 /// Provides information about the the server-side resource consumption that
235 /// is related to this `ResultSet` object.
236 pub async fn server_usage(&self) -> ServerUsage {
237 *self.state.lock_async().await.server_usage()
238 }
239}
240
241impl std::fmt::Display for ResultSet {
242 // Writes a header and then the data
243 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
244 writeln!(fmt, "{}\n", &self.metadata)?;
245 // hard to do, because of the async lock we'd need to acquire
246 writeln!(fmt, "Display not implemented for async result set\n")?;
247
248 Ok(())
249 }
250}