tiberius/
result.rs

1pub use crate::tds::stream::{QueryItem, ResultMetadata};
2use crate::{
3    client::Connection,
4    tds::stream::{ReceivedToken, TokenStream},
5};
6use futures_util::io::{AsyncRead, AsyncWrite};
7use futures_util::stream::TryStreamExt;
8use std::fmt::Debug;
9
10/// A result from a query execution, listing the number of affected rows.
11///
12/// If executing multiple queries, the resulting counts will be come separately,
13/// marking the rows affected for each query.
14///
15/// # Example
16///
17/// ```no_run
18/// # use tiberius::Config;
19/// # use tokio_util::compat::TokioAsyncWriteCompatExt;
20/// # use std::env;
21/// # #[tokio::main]
22/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
23/// # let c_str = env::var("TIBERIUS_TEST_CONNECTION_STRING").unwrap_or(
24/// #     "server=tcp:localhost,1433;integratedSecurity=true;TrustServerCertificate=true".to_owned(),
25/// # );
26/// # let config = Config::from_ado_string(&c_str)?;
27/// # let tcp = tokio::net::TcpStream::connect(config.get_addr()).await?;
28/// # tcp.set_nodelay(true)?;
29/// # let mut client = tiberius::Client::connect(config, tcp.compat_write()).await?;
30/// let result = client
31///     .execute(
32///         "INSERT INTO #Test (id) VALUES (@P1); INSERT INTO #Test (id) VALUES (@P2, @P3)",
33///         &[&1i32, &2i32, &3i32],
34///     )
35///     .await?;
36///
37/// assert_eq!(&[1, 2], result.rows_affected());
38/// # Ok(())
39/// # }
40/// ```
41///
42/// [`Client`]: struct.Client.html
43/// [`Rows`]: struct.Row.html
44/// [`next_resultset`]: #method.next_resultset
45#[derive(Debug)]
46pub struct ExecuteResult {
47    rows_affected: Vec<u64>,
48}
49
50impl<'a> ExecuteResult {
51    pub(crate) async fn new<S: AsyncRead + AsyncWrite + Unpin + Send>(
52        connection: &'a mut Connection<S>,
53    ) -> crate::Result<Self> {
54        let mut token_stream = TokenStream::new(connection).try_unfold();
55        let mut rows_affected = Vec::new();
56
57        while let Some(token) = token_stream.try_next().await? {
58            match token {
59                ReceivedToken::DoneProc(done) if done.is_final() => (),
60                ReceivedToken::DoneProc(done) => rows_affected.push(done.rows()),
61                ReceivedToken::DoneInProc(done) => rows_affected.push(done.rows()),
62                ReceivedToken::Done(done) => rows_affected.push(done.rows()),
63                _ => (),
64            }
65        }
66
67        Ok(Self { rows_affected })
68    }
69
70    /// A slice of numbers of rows affected in the same order as the given
71    /// queries.
72    pub fn rows_affected(&self) -> &[u64] {
73        self.rows_affected.as_slice()
74    }
75
76    /// Aggregates all resulting row counts into a sum.
77    ///
78    /// # Example
79    ///
80    /// ```no_run
81    /// # use tiberius::Config;
82    /// # use tokio_util::compat::TokioAsyncWriteCompatExt;
83    /// # use std::env;
84    /// # #[tokio::main]
85    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
86    /// # let c_str = env::var("TIBERIUS_TEST_CONNECTION_STRING").unwrap_or(
87    /// #     "server=tcp:localhost,1433;integratedSecurity=true;TrustServerCertificate=true".to_owned(),
88    /// # );
89    /// # let config = Config::from_ado_string(&c_str)?;
90    /// # let tcp = tokio::net::TcpStream::connect(config.get_addr()).await?;
91    /// # tcp.set_nodelay(true)?;
92    /// # let mut client = tiberius::Client::connect(config, tcp.compat_write()).await?;
93    /// let rows_affected = client
94    ///     .execute(
95    ///         "INSERT INTO #Test (id) VALUES (@P1); INSERT INTO #Test (id) VALUES (@P2, @P3)",
96    ///         &[&1i32, &2i32, &3i32],
97    ///     )
98    ///     .await?;
99    ///
100    /// assert_eq!(3, rows_affected.total());
101    /// # Ok(())
102    /// # }
103    pub fn total(self) -> u64 {
104        self.rows_affected.into_iter().sum()
105    }
106}
107
108impl IntoIterator for ExecuteResult {
109    type Item = u64;
110    type IntoIter = std::vec::IntoIter<Self::Item>;
111
112    fn into_iter(self) -> Self::IntoIter {
113        self.rows_affected.into_iter()
114    }
115}