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}