Repository: blackbeam/mysql_async (language: rust)

Issue:
Support info field on `OkPacket`
It would be useful to have access to `OkPacket::info_ref` similar to `last_insert_id` and `affected_rows`. For reference: https://docs.rs/mysql_common/0.20.1/mysql_common/packets/struct.OkPacket.html#method.info_ref

I am needing this info to parse the rows matched by an `UPDATE` query, that won't necessarily be affected.


Discussion / hints:

Fix patch:
diff --git a/src/conn/mod.rs b/src/conn/mod.rs
index a86c35cc..61f7b831 100644
--- a/src/conn/mod.rs
+++ b/src/conn/mod.rs
@@ -13,11 +13,12 @@ use mysql_common::{
     crypto,
     packets::{
         parse_auth_switch_request, parse_handshake_packet, AuthPlugin, AuthSwitchRequest,
-        HandshakeResponse, SslRequest,
+        HandshakeResponse, OkPacket, SslRequest,
     },
 };
 
 use std::{
+    borrow::Cow,
     fmt,
     future::Future,
     mem,
@@ -76,9 +77,7 @@ struct ConnInner {
     socket: Option<String>,
     capabilities: consts::CapabilityFlags,
     status: consts::StatusFlags,
-    last_insert_id: u64,
-    affected_rows: u64,
-    warnings: u16,
+    last_ok_packet: Option<OkPacket<'static>>,
     pool: Option<Pool>,
     has_result: Option<(Arc<Vec<Column>>, Option<StmtCacheResult>)>,
     in_transaction: bool,
@@ -113,11 +112,9 @@ impl ConnInner {
         ConnInner {
             capabilities: opts.get_capabilities(),
             status: consts::StatusFlags::empty(),
-            last_insert_id: 0,
-            affected_rows: 0,
+            last_ok_packet: None,
             stream: None,
             max_allowed_packet: DEFAULT_MAX_ALLOWED_PACKET,
-            warnings: 0,
             version: (0, 0, 0),
             id: 0,
             has_result: None,
@@ -566,7 +563,11 @@ impl ConnectionLike for Conn {
     }
 
     fn get_affected_rows(&self) -> u64 {
-        self.inner.affected_rows
+        self.inner
+            .last_ok_packet
+            .as_ref()
+            .map(|ok| ok.affected_rows())
+            .unwrap_or_default()
     }
 
     fn get_capabilities(&self) -> consts::CapabilityFlags {
@@ -578,10 +579,26 @@ impl ConnectionLike for Conn {
     }
 
     fn get_last_insert_id(&self) -> Option<u64> {
-        match self.inner.last_insert_id {
-            0 => None,
-            x => Some(x),
-        }
+        self.inner
+            .last_ok_packet
+            .as_ref()
+            .and_then(|ok| ok.last_insert_id())
+    }
+
+    fn get_info(&self) -> Cow<'_, str> {
+        self.inner
+            .last_ok_packet
+            .as_ref()
+            .and_then(|ok| ok.info_str())
+            .unwrap_or_else(|| "".into())
+    }
+
+    fn get_warnings(&self) -> u16 {
+        self.inner
+            .last_ok_packet
+            .as_ref()
+            .map(|ok| ok.warnings())
+            .unwrap_or_default()
     }
 
     fn get_local_infile_handler(&self) -> Option<Arc<dyn LocalInfileHandler>> {
@@ -608,18 +625,14 @@ impl ConnectionLike for Conn {
         self.inner.status
     }
 
-    fn set_affected_rows(&mut self, affected_rows: u64) {
-        self.inner.affected_rows = affected_rows;
+    fn set_last_ok_packet(&mut self, ok_packet: Option<OkPacket<'static>>) {
+        self.inner.last_ok_packet = ok_packet;
     }
 
     fn set_in_transaction(&mut self, in_transaction: bool) {
         self.inner.in_transaction = in_transaction;
     }
 
-    fn set_last_insert_id(&mut self, last_insert_id: u64) {
-        self.inner.last_insert_id = last_insert_id;
-    }
-
     fn set_pending_result(&mut self, meta: Option<(Arc<Vec<Column>>, Option<StmtCacheResult>)>) {
         self.inner.has_result = meta;
     }
@@ -628,10 +641,6 @@ impl ConnectionLike for Conn {
         self.inner.status = status;
     }
 
-    fn set_warnings(&mut self, warnings: u16) {
-        self.inner.warnings = warnings;
-    }
-
     fn reset_seq_id(&mut self) {
         if let Some(stream) = self.inner.stream.as_mut() {
             stream.reset_seq_id();
diff --git a/src/conn/pool/futures/disconnect_pool.rs b/src/conn/pool/futures/disconnect_pool.rs
index 67d82612..67421242 100644
--- a/src/conn/pool/futures/disconnect_pool.rs
+++ b/src/conn/pool/futures/disconnect_pool.rs
@@ -6,9 +6,11 @@
 // option. All files in the project carrying such notice may not be copied,
 // modified, or distributed except according to those terms.
 
-use std::future::Future;
-use std::pin::Pin;
-use std::task::{Context, Poll};
+use std::{
+    future::Future,
+    pin::Pin,
+    task::{Context, Poll},
+};
 
 use crate::{
     conn::pool::{Inner, Pool},
diff --git a/src/conn/pool/futures/get_conn.rs b/src/conn/pool/futures/get_conn.rs
index dd1a7a82..9b668125 100644
--- a/src/conn/pool/futures/get_conn.rs
+++ b/src/conn/pool/futures/get_conn.rs
@@ -7,9 +7,11 @@
 // modified, or distributed except according to those terms.
 
 use futures_core::ready;
-use std::future::Future;
-use std::pin::Pin;
-use std::task::{Context, Poll};
+use std::{
+    future::Future,
+    pin::Pin,
+    task::{Context, Poll},
+};
 
 use crate::{
     conn::{pool::Pool, Conn},
diff --git a/src/conn/pool/mod.rs b/src/conn/pool/mod.rs
index 23de3b89..88bba3eb 100644
--- a/src/conn/pool/mod.rs
+++ b/src/conn/pool/mod.rs
@@ -275,8 +275,7 @@ impl Drop for Conn {
 
 #[cfg(test)]
 mod test {
-    use futures_util::stream::StreamExt;
-    use futures_util::{future::try_join_all, try_join};
+    use futures_util::{future::try_join_all, stream::StreamExt, try_join};
 
     use std::time::Duration;
 
@@ -619,13 +618,10 @@ mod test {
 
     #[cfg(feature = "nightly")]
     mod bench {
-        use futures_util::future::FutureExt;
-        use futures_util::try_future::TryFutureExt;
+        use futures_util::{future::FutureExt, try_future::TryFutureExt};
         use tokio::runtime::Runtime;
 
-        use crate::prelude::Queryable;
-        use crate::test_misc::get_opts;
-        use crate::{Pool, PoolConstraints, PoolOptions};
+        use crate::{prelude::Queryable, test_misc::get_opts, Pool, PoolConstraints, PoolOptions};
         use std::time::Duration;
 
         #[bench]
diff --git a/src/conn/pool/recycler.rs b/src/conn/pool/recycler.rs
index 8f6b04d2..439e628a 100644
--- a/src/conn/pool/recycler.rs
+++ b/src/conn/pool/recycler.rs
@@ -10,11 +10,12 @@ use futures_core::stream::Stream;
 use futures_util::stream::futures_unordered::FuturesUnordered;
 use tokio::sync::mpsc;
 
-use std::future::Future;
-use std::pin::Pin;
-use std::sync::atomic::Ordering;
-use std::sync::Arc;
-use std::task::{Context, Poll};
+use std::{
+    future::Future,
+    pin::Pin,
+    sync::{atomic::Ordering, Arc},
+    task::{Context, Poll},
+};
 
 use super::{IdlingConn, Inner};
 use crate::{BoxFuture, Conn, PoolOptions};
diff --git a/src/conn/pool/ttl_check_inerval.rs b/src/conn/pool/ttl_check_inerval.rs
index 8c2a2765..04a28fbe 100644
--- a/src/conn/pool/ttl_check_inerval.rs
+++ b/src/conn/pool/ttl_check_inerval.rs
@@ -6,18 +6,20 @@
 // option. All files in the project carrying such notice may not be copied,
 // modified, or distributed except according to those terms.
 
-use futures_util::future::FutureExt;
-use futures_util::stream::{StreamExt, StreamFuture};
+use futures_util::{
+    future::FutureExt,
+    stream::{StreamExt, StreamFuture},
+};
 use pin_project::pin_project;
 use tokio::time::{self, Interval};
 
-use std::future::Future;
-use std::sync::atomic::Ordering;
-use std::sync::Arc;
+use std::{
+    future::Future,
+    sync::{atomic::Ordering, Arc},
+};
 
 use super::Inner;
-use crate::prelude::Queryable;
-use crate::PoolOptions;
+use crate::{prelude::Queryable, PoolOptions};
 use futures_core::task::{Context, Poll};
 use std::pin::Pin;
 
diff --git a/src/connection_like/mod.rs b/src/connection_like/mod.rs
index 9cb927f7..f4ea43fa 100644
--- a/src/connection_like/mod.rs
+++ b/src/connection_like/mod.rs
@@ -9,11 +9,11 @@
 use futures_util::future::ok;
 use mysql_common::{
     io::ReadMysqlExt,
-    packets::{column_from_payload, parse_local_infile_packet, Column, ComStmtClose},
+    packets::{column_from_payload, parse_local_infile_packet, Column, ComStmtClose, OkPacket},
 };
 use tokio::prelude::*;
 
-use std::sync::Arc;
+use std::{borrow::Cow, sync::Arc};
 
 use crate::{
     conn::{named_params::parse_named_params, stmt_cache::StmtCache},
@@ -110,6 +110,14 @@ where
         self.conn_like_ref().get_last_insert_id()
     }
 
+    fn get_info(&self) -> Cow<'_, str> {
+        self.conn_like_ref().get_info()
+    }
+
+    fn get_warnings(&self) -> u16 {
+        self.conn_like_ref().get_warnings()
+    }
+
     fn get_local_infile_handler(&self) -> Option<Arc<dyn LocalInfileHandler>> {
         self.conn_like_ref().get_local_infile_handler()
     }
@@ -134,18 +142,14 @@ where
         self.conn_like_ref().get_status()
     }
 
-    fn set_affected_rows(&mut self, affected_rows: u64) {
-        self.conn_like_mut().set_affected_rows(affected_rows);
+    fn set_last_ok_packet(&mut self, ok_packet: Option<OkPacket<'static>>) {
+        self.conn_like_mut().set_last_ok_packet(ok_packet);
     }
 
     fn set_in_transaction(&mut self, in_transaction: bool) {
         self.conn_like_mut().set_in_transaction(in_transaction);
     }
 
-    fn set_last_insert_id(&mut self, last_insert_id: u64) {
-        self.conn_like_mut().set_last_insert_id(last_insert_id);
-    }
-
     fn set_pending_result(&mut self, meta: Option<(Arc<Vec<Column>>, Option<StmtCacheResult>)>) {
         self.conn_like_mut().set_pending_result(meta);
     }
@@ -154,10 +158,6 @@ where
         self.conn_like_mut().set_status(status);
     }
 
-    fn set_warnings(&mut self, warnings: u16) {
-        self.conn_like_mut().set_warnings(warnings);
-    }
-
     fn reset_seq_id(&mut self) {
         self.conn_like_mut().reset_seq_id();
     }
@@ -186,18 +186,18 @@ pub trait ConnectionLike: Send {
     fn get_capabilities(&self) -> CapabilityFlags;
     fn get_in_transaction(&self) -> bool;
     fn get_last_insert_id(&self) -> Option<u64>;
+    fn get_info(&self) -> Cow<'_, str>;
+    fn get_warnings(&self) -> u16;
     fn get_local_infile_handler(&self) -> Option<Arc<dyn LocalInfileHandler>>;
     fn get_max_allowed_packet(&self) -> usize;
     fn get_opts(&self) -> &Opts;
     fn get_pending_result(&self) -> Option<&(Arc<Vec<Column>>, Option<StmtCacheResult>)>;
     fn get_server_version(&self) -> (u16, u16, u16);
     fn get_status(&self) -> StatusFlags;
-    fn set_affected_rows(&mut self, affected_rows: u64);
+    fn set_last_ok_packet(&mut self, ok_packet: Option<OkPacket<'static>>);
     fn set_in_transaction(&mut self, in_transaction: bool);
-    fn set_last_insert_id(&mut self, last_insert_id: u64);
     fn set_pending_result(&mut self, meta: Option<(Arc<Vec<Column>>, Option<StmtCacheResult>)>);
     fn set_status(&mut self, status: StatusFlags);
-    fn set_warnings(&mut self, warnings: u16);
     fn reset_seq_id(&mut self);
     fn sync_seq_id(&mut self);
     fn touch(&mut self) -> ();
diff --git a/src/connection_like/read_packet.rs b/src/connection_like/read_packet.rs
index 2a40f32f..b1ec3b91 100644
--- a/src/connection_like/read_packet.rs
+++ b/src/connection_like/read_packet.rs
@@ -10,12 +10,15 @@ use futures_core::ready;
 use futures_util::stream::{StreamExt, StreamFuture};
 use mysql_common::packets::{parse_err_packet, parse_ok_packet};
 use pin_project::pin_project;
-use std::future::Future;
-use std::pin::Pin;
-use std::task::{Context, Poll};
+use std::{
+    future::Future,
+    pin::Pin,
+    task::{Context, Poll},
+};
 
 use crate::{
     connection_like::{streamless::Streamless, ConnectionLike},
+    consts::StatusFlags,
     error::*,
     io,
 };
@@ -48,13 +51,13 @@ impl<T: ConnectionLike> Future for ReadPacket<T> {
         match packet_opt {
             Some(packet) => {
                 if let Ok(ok_packet) = parse_ok_packet(&*packet, conn_like.get_capabilities()) {
-                    conn_like.set_affected_rows(ok_packet.affected_rows());
-                    conn_like.set_last_insert_id(ok_packet.last_insert_id().unwrap_or(0));
                     conn_like.set_status(ok_packet.status_flags());
-                    conn_like.set_warnings(ok_packet.warnings());
+                    conn_like.set_last_ok_packet(Some(ok_packet.into_owned()));
                 } else if let Ok(err_packet) =
                     parse_err_packet(&*packet, conn_like.get_capabilities())
                 {
+                    conn_like.set_status(StatusFlags::empty());
+                    conn_like.set_last_ok_packet(None);
                     return Err(err_packet.into()).into();
                 }
 
diff --git a/src/connection_like/write_packet.rs b/src/connection_like/write_packet.rs
index 49872eee..67e5befc 100644
--- a/src/connection_like/write_packet.rs
+++ b/src/connection_like/write_packet.rs
@@ -8,9 +8,11 @@
 
 use futures_core::ready;
 use pin_project::pin_project;
-use std::future::Future;
-use std::pin::Pin;
-use std::task::{Context, Poll};
+use std::{
+    future::Future,
+    pin::Pin,
+    task::{Context, Poll},
+};
 
 use crate::{
     connection_like::{streamless::Streamless, ConnectionLike},
diff --git a/src/io/async_tls.rs b/src/io/async_tls.rs
index fb873de8..def5f755 100644
--- a/src/io/async_tls.rs
+++ b/src/io/async_tls.rs
@@ -12,10 +12,11 @@
 use bytes::buf::BufMut;
 use native_tls::{Error, TlsConnector};
 use pin_project::pin_project;
-use std::pin::Pin;
-use std::task::{Context, Poll};
-use tokio::io::Error as IoError;
-use tokio::prelude::*;
+use std::{
+    pin::Pin,
+    task::{Context, Poll},
+};
+use tokio::{io::Error as IoError, prelude::*};
 use tokio_tls::{self};
 
 use std::mem::MaybeUninit;
diff --git a/src/io/futures/write_packet.rs b/src/io/futures/write_packet.rs
index e3ac8adb..b5b44ed5 100644
--- a/src/io/futures/write_packet.rs
+++ b/src/io/futures/write_packet.rs
@@ -8,9 +8,11 @@
 
 use futures_core::ready;
 use futures_sink::Sink;
-use std::future::Future;
-use std::pin::Pin;
-use std::task::{Context, Poll};
+use std::{
+    future::Future,
+    pin::Pin,
+    task::{Context, Poll},
+};
 
 use crate::{error::*, io::Stream};
 
diff --git a/src/io/mod.rs b/src/io/mod.rs
index ecb17074..737ef822 100644
--- a/src/io/mod.rs
+++ b/src/io/mod.rs
@@ -11,8 +11,7 @@ use futures_core::{ready, stream};
 use mysql_common::proto::codec::PacketCodec as PacketCodecInner;
 use native_tls::{Certificate, Identity, TlsConnector};
 use pin_project::{pin_project, project};
-use tokio::net::TcpStream;
-use tokio::prelude::*;
+use tokio::{net::TcpStream, prelude::*};
 use tokio_util::codec::{Decoder, Encoder, Framed, FramedParts};
 
 use std::{
diff --git a/src/io/socket.rs b/src/io/socket.rs
index 6049109b..bbaa281d 100644
--- a/src/io/socket.rs
+++ b/src/io/socket.rs
@@ -8,10 +8,11 @@
 
 use bytes::BufMut;
 use pin_project::pin_project;
-use std::pin::Pin;
-use std::task::{Context, Poll};
-use tokio::io::Error;
-use tokio::prelude::*;
+use std::{
+    pin::Pin,
+    task::{Context, Poll},
+};
+use tokio::{io::Error, prelude::*};
 
 use std::{io, mem::MaybeUninit, path::Path};
 
diff --git a/src/local_infile_handler/mod.rs b/src/local_infile_handler/mod.rs
index a471a4c5..5095eea9 100644
--- a/src/local_infile_handler/mod.rs
+++ b/src/local_infile_handler/mod.rs
@@ -9,10 +9,7 @@
 use crate::error;
 use tokio::prelude::*;
 
-use std::future::Future;
-use std::marker::Unpin;
-use std::pin::Pin;
-use std::{fmt, sync::Arc};
+use std::{fmt, future::Future, marker::Unpin, pin::Pin, sync::Arc};
 
 pub mod builtin;
 
diff --git a/src/queryable/mod.rs b/src/queryable/mod.rs
index 3838a41f..4bd31a27 100644
--- a/src/queryable/mod.rs
+++ b/src/queryable/mod.rs
@@ -85,7 +85,7 @@ where
         })
     }
 
-    /// Returns future that disconnects this connection from a server.
+    /// Returns future, that disconnects this connection from a server.
     fn disconnect(mut self) -> BoxFuture<()> {
         self.on_disconnect();
         let f = self.write_command_data(Command::COM_QUIT, &[]);
diff --git a/src/queryable/query_result/mod.rs b/src/queryable/query_result/mod.rs
index 90cf3739..7716792f 100644
--- a/src/queryable/query_result/mod.rs
+++ b/src/queryable/query_result/mod.rs
@@ -9,9 +9,7 @@
 use futures_util::future::Either;
 use mysql_common::row::convert::FromRowError;
 
-use std::marker::PhantomData;
-use std::result::Result as StdResult;
-use std::sync::Arc;
+use std::{borrow::Cow, marker::PhantomData, result::Result as StdResult, sync::Arc};
 
 use self::QueryResultInner::*;
 use crate::{
@@ -165,16 +163,26 @@ where
         }
     }
 
-    /// Last insert id (if not 0).
+    /// Last insert id, if any.
     pub fn last_insert_id(&self) -> Option<u64> {
         self.get_last_insert_id()
     }
 
-    /// Value of `affected_rows` returned from a server.
+    /// Number of affected rows, reported by the server, or `0`.
     pub fn affected_rows(&self) -> u64 {
         self.get_affected_rows()
     }
 
+    /// Text information, reported by the server, or an empty string
+    pub fn info(&self) -> Cow<'_, str> {
+        self.get_info()
+    }
+
+    /// Number of warnings, reported by the server, or `0`.
+    pub fn warnings(&self) -> u16 {
+        self.get_warnings()
+    }
+
     /// `true` if there is no more rows nor result sets in this query.
     ///
     /// One could use it to check if there is more than one result set in this query result.
diff --git a/src/queryable/stmt.rs b/src/queryable/stmt.rs
index 42cfd490..c5729ecd 100644
--- a/src/queryable/stmt.rs
+++ b/src/queryable/stmt.rs
@@ -19,8 +19,10 @@ use crate::{
     Column, Params, Row,
     Value::{self},
 };
-use mysql_common::constants::MAX_PAYLOAD_LEN;
-use mysql_common::packets::{parse_stmt_packet, ComStmtExecuteRequestBuilder, ComStmtSendLongData};
+use mysql_common::{
+    constants::MAX_PAYLOAD_LEN,
+    packets::{parse_stmt_packet, ComStmtExecuteRequestBuilder, ComStmtSendLongData},
+};
 
 /// Inner statement representation.
 #[derive(Eq, PartialEq, Clone, Debug)]


Test patch:
diff --git a/tests/generic.rs b/tests/generic.rs
index 0ee875f3..bee1fa5b 100644
--- a/tests/generic.rs
+++ b/tests/generic.rs
@@ -6,12 +6,9 @@
 // option. All files in the project carrying such notice may not be copied,
 // modified, or distributed except according to those terms.
 
-use mysql_async::error::*;
-use mysql_async::prelude::*;
-use mysql_async::{Opts, Pool, QueryResult};
+use mysql_async::{error::*, prelude::*, Opts, Pool, QueryResult};
 
-use std::env;
-use std::io;
+use std::{env, io};
 
 fn get_url() -> String {
     if let Ok(url) = env::var("DATABASE_URL") {