diff --git a/Cargo.toml b/Cargo.toml index 269f44a..c4bf4c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,13 +31,18 @@ default = [ # It is necessary to choose one of `flate2` backends. "flate2/zlib", + # set of enabled-by-default mysql_common features "mysql_common/bigdecimal03", "mysql_common/rust_decimal", "mysql_common/time03", "mysql_common/uuid", "mysql_common/frunk", + + # use global buffer pool by default + "buffer-pool", ] rustls-tls = ["rustls", "webpki", "webpki-roots", "rustls-pemfile"] +buffer-pool = [] nightly = [] [dev-dependencies] diff --git a/README.md b/README.md index 85886b2..6024fc6 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,44 @@ assert_eq!(payments, selected_payments); println!("Yay!"); ``` +### Crate Features + +* crate's features: + + * **native-tls** (enabled by default) – specifies `native-tls` as the TLS backend + (see the [SSL Support](#ssl-support) section) + * **rustls-tls** (disabled by default) – specifies `rustls` as the TLS backend + (see the [SSL Support](#ssl-support) section) + * **buffer-pool** (enabled by default) – enables buffer pooling + (see the [Buffer Pool](#buffer-pool) section) + +* external features enabled by default: + + * for the `flate2` crate (please consult `flate2` crate documentation for available features): + + * **flate2/zlib** (necessary) – `zlib` backend is chosed by default. + + * for the `mysql_common` crate (please consult `mysql_common` crate documentation for available features): + + * **mysql_common/bigdecimal03** – the `bigdecimal03` is enabled by default + * **mysql_common/rust_decimal** – the `rust_decimal` is enabled by default + * **mysql_common/time03** – the `time03` is enabled by default + * **mysql_common/uuid** – the `uuid` is enabled by default + * **mysql_common/frunk** – the `frunk` is enabled by default + +Please note, that you'll need to reenable external features if you are using `no-default-features = true`: + +```toml +[dependencies] +# Lets say that we want to use the `rustls-tls` feature: +mysql = { version = "*", no-default-features = true, features = ["rustls-tls", "buffer-pool"] } +# Previous line disables default mysql features, +# so now we have to choose the flate2 backend (this is necessary), +# as well as the desired set of mysql_common features: +flate2 = { version = "*", no-default-features = true, features = ["zlib"] } +mysql_common = { version = "*", no-default-features = true, features = ["bigdecimal03", "time03", "uuid"]} +``` + ### API Documentation Please refer to the [crate docs]. @@ -435,7 +473,7 @@ let structure: Deserialized = from_value(value); assert_eq!(structure, Deserialized(Example { foo: 42 })); ``` -#### `QueryResult` +#### [`QueryResult`] It's an iterator over rows of a query result with support of multi-result sets. It's intended for cases when you need full control during result set iteration. For other cases @@ -444,7 +482,7 @@ the first result set and drop everything else. This iterator is lazy so it won't read the result from server until you iterate over it. MySql protocol is strictly sequential, so `Conn` will be mutably borrowed until the result -is fully consumed. +is fully consumed (please also look at [`QueryResult::iter`] docs). ```rust use mysql::*; @@ -456,7 +494,7 @@ let mut conn = Conn::new(get_opts())?; let mut result = conn.query_iter("SELECT 1, 2; SELECT 3, 3.14;")?; let mut sets = 0; -while let Some(result_set) = result.current_set() { +while let Some(result_set) = result.iter() { sets += 1; println!("Result set columns: {:?}", result_set.columns()); @@ -637,15 +675,14 @@ that helps to avoid allocations for basic scenarios. You can control it's charac the following environment variables: * `RUST_MYSQL_BUFFER_POOL_CAP` (defaults to 128) – controls the pool capacity. Dropped buffer will - be immediately deallocated if the pool is full. - - **Note:** it might be the case, that you don't need the pooling (say you are using jemalloc). - It's possible to disable the pool by setting the `RUST_MYSQL_BUFFER_POOL_CAP` environment - variable to `0`. + be immediately deallocated if the pool is full. Set it to `0` to disable the pool at runtime. * `RUST_MYSQL_BUFFER_SIZE_CAP` (defaults to 4MiB) – controls the maximum capacity of a buffer stored in the pool. Capacity of a dropped buffer will be shrinked to this value when buffer - is returning to the pool. + is returned to the pool. + +To completely disable the pool (say you are using jemalloc) please remove the `buffer-pool` feature +from the set of default crate features (see the [Crate Features](#crate-features) section). #### `BinQuery` and `BatchQuery` traits. @@ -725,17 +762,9 @@ SSL support comes in two flavors: 1. Based on **native-tls** – this is the default option, that usually works without pitfalls (see the `native-tls` crate feature). 2. Based on **rustls** – TLS backend written in Rust. Please use the `rustls-tls` crate feature - to enable: - - ```toml - [dependencies] - mysql = { version = "*", no-default-features = true, features = ["rustls-tls"] } - # Please note, that the previous line disables default mysql features, - # so now we have to choose the flate2 backend (this is necessary): - flate2 = { version = "1.0", no-default-features = true, features = ["zlib"] } - ``` + to enable it (see the [Crate Features](#crate-features) section). - Please also note a few things about this backend: + Please also note a few things about **rustls**: * it will fail if you'll try to connect to the server by its IP address, hostname is required; * it, most likely, won't work on windows, at least with default server certs, generated by the diff --git a/src/buffer_pool/disabled.rs b/src/buffer_pool/disabled.rs new file mode 100644 index 0000000..16f61fe --- /dev/null +++ b/src/buffer_pool/disabled.rs @@ -0,0 +1,25 @@ +#![cfg(not(feature = "buffer-pool"))] + +use std::ops::Deref; + +#[derive(Debug)] +#[repr(transparent)] +pub struct Buffer(Vec); + +impl AsMut> for Buffer { + fn as_mut(&mut self) -> &mut Vec { + &mut self.0 + } +} + +impl Deref for Buffer { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + self.0.deref() + } +} + +pub const fn get_buffer() -> Buffer { + Buffer(Vec::new()) +} diff --git a/src/buffer_pool.rs b/src/buffer_pool/enabled.rs similarity index 72% rename from src/buffer_pool.rs rename to src/buffer_pool/enabled.rs index 8d9f2d0..5d08d21 100644 --- a/src/buffer_pool.rs +++ b/src/buffer_pool/enabled.rs @@ -1,18 +1,20 @@ -// Copyright (c) 2021 Anatoly Ikorsky -// -// Licensed under the Apache License, Version 2.0 -// or the MIT -// license , at your -// option. All files in the project carrying such notice may not be copied, -// modified, or distributed except according to those terms. +#![cfg(feature = "buffer-pool")] use crossbeam::queue::ArrayQueue; +use once_cell::sync::Lazy; use std::{mem::replace, ops::Deref, sync::Arc}; const DEFAULT_MYSQL_BUFFER_POOL_CAP: usize = 128; const DEFAULT_MYSQL_BUFFER_SIZE_CAP: usize = 4 * 1024 * 1024; +static BUFFER_POOL: Lazy> = Lazy::new(|| Default::default()); + +#[inline(always)] +pub fn get_buffer() -> Buffer { + BUFFER_POOL.get() +} + #[derive(Debug)] struct Inner { buffer_cap: usize, @@ -20,7 +22,7 @@ struct Inner { } impl Inner { - fn get(self: &Arc) -> PooledBuf { + fn get(self: &Arc) -> Buffer { let mut buf = self.pool.pop().unwrap_or_default(); // SAFETY: @@ -28,7 +30,7 @@ impl Inner { // 2. OK - nothing to initialize unsafe { buf.set_len(0) } - PooledBuf(buf, Some(self.clone())) + Buffer(buf, Some(self.clone())) } fn put(&self, mut buf: Vec) { @@ -61,10 +63,10 @@ impl BufferPool { })) } - pub fn get(self: &Arc) -> PooledBuf { + pub fn get(self: &Arc) -> Buffer { match self.0 { Some(ref inner) => inner.get(), - None => PooledBuf(Vec::new(), None), + None => Buffer(Vec::new(), None), } } } @@ -76,15 +78,15 @@ impl Default for BufferPool { } #[derive(Debug)] -pub struct PooledBuf(Vec, Option>); +pub struct Buffer(Vec, Option>); -impl AsMut> for PooledBuf { +impl AsMut> for Buffer { fn as_mut(&mut self) -> &mut Vec { &mut self.0 } } -impl Deref for PooledBuf { +impl Deref for Buffer { type Target = [u8]; fn deref(&self) -> &Self::Target { @@ -92,7 +94,7 @@ impl Deref for PooledBuf { } } -impl Drop for PooledBuf { +impl Drop for Buffer { fn drop(&mut self) { if let Some(ref inner) = self.1 { inner.put(replace(&mut self.0, vec![])); diff --git a/src/buffer_pool/mod.rs b/src/buffer_pool/mod.rs new file mode 100644 index 0000000..95565e2 --- /dev/null +++ b/src/buffer_pool/mod.rs @@ -0,0 +1,16 @@ +// Copyright (c) 2021 Anatoly Ikorsky +// +// Licensed under the Apache License, Version 2.0 +// or the MIT +// license , at your +// option. All files in the project carrying such notice may not be copied, +// modified, or distributed except according to those terms. + +mod disabled; +mod enabled; + +#[cfg(feature = "buffer-pool")] +pub use enabled::{get_buffer, Buffer}; + +#[cfg(not(feature = "buffer-pool"))] +pub use disabled::{get_buffer, Buffer}; diff --git a/src/conn/mod.rs b/src/conn/mod.rs index cc2899e..f624fb0 100644 --- a/src/conn/mod.rs +++ b/src/conn/mod.rs @@ -41,7 +41,7 @@ use std::{ }; use crate::{ - buffer_pool::PooledBuf, + buffer_pool::{get_buffer, Buffer}, conn::{ local_infile::LocalInfile, pool::{Pool, PooledConn}, @@ -410,9 +410,9 @@ impl Conn { Ok(()) } - fn read_packet(&mut self) -> Result { + fn read_packet(&mut self) -> Result { loop { - let mut buffer = crate::BUFFER_POOL.get(); + let mut buffer = get_buffer(); if !self.stream_mut().next_packet(buffer.as_mut())? { return Err( io::Error::new(io::ErrorKind::BrokenPipe, "server disconnected").into(), @@ -441,7 +441,7 @@ impl Conn { } fn write_struct(&mut self, s: &T) -> Result<()> { - let mut buf = crate::BUFFER_POOL.get(); + let mut buf = get_buffer(); s.serialize(buf.as_mut()); self.write_packet(&mut &*buf) } @@ -462,7 +462,7 @@ impl Conn { fn handle_ok<'a, T: OkPacketKind>( &mut self, - buffer: &'a PooledBuf, + buffer: &'a Buffer, ) -> crate::Result> { let ok = ParseBuf(&**buffer) .parse::>(self.0.capability_flags)? @@ -652,7 +652,7 @@ impl Conn { Some(self.connect_attrs().clone()), ); - let mut buf = crate::BUFFER_POOL.get(); + let mut buf = get_buffer(); handshake_response.serialize(buf.as_mut()); self.write_packet(&mut &*buf) } @@ -771,7 +771,7 @@ impl Conn { } fn write_command_raw(&mut self, cmd: &T) -> Result<()> { - let mut buf = crate::BUFFER_POOL.get(); + let mut buf = get_buffer(); cmd.serialize(buf.as_mut()); self.reset_seq_id(); debug_assert!(buf.len() > 0); @@ -780,7 +780,7 @@ impl Conn { } fn write_command(&mut self, cmd: Command, data: &[u8]) -> Result<()> { - let mut buf = crate::BUFFER_POOL.get(); + let mut buf = get_buffer(); buf.as_mut().put_u8(cmd as u8); buf.as_mut().extend_from_slice(data); @@ -1713,7 +1713,7 @@ mod test { ) .unwrap(); - while let Some(result) = query_result.current_set() { + while let Some(result) = query_result.iter() { result.affected_rows(); } } @@ -1752,7 +1752,7 @@ mod test { } let mut result = conn.query_iter("SELECT 1; SELECT 2; SELECT 3;").unwrap(); let mut i = 0; - while let Some(result_set) = result.current_set() { + while let Some(result_set) = result.iter() { i += 1; for row in result_set { match i { diff --git a/src/conn/query.rs b/src/conn/query.rs index 4911ae9..bccea2f 100644 --- a/src/conn/query.rs +++ b/src/conn/query.rs @@ -384,7 +384,7 @@ where let params = params.into(); let meta = conn._execute(&*statement, params)?; let mut query_result = QueryResult::::new((&mut *conn).into(), meta); - while let Some(result_set) = query_result.current_set() { + while let Some(result_set) = query_result.iter() { for row in result_set { row?; } diff --git a/src/conn/query_result.rs b/src/conn/query_result.rs index 341bec2..e79bfd6 100644 --- a/src/conn/query_result.rs +++ b/src/conn/query_result.rs @@ -153,9 +153,68 @@ impl<'c, 't, 'tc, T: crate::prelude::Protocol> QueryResult<'c, 't, 'tc, T> { } /// Returns an iterator over the current result set. + #[deprecated = "Please use QueryResult::iter"] + pub fn next_set<'d>(&'d mut self) -> Option> { + self.iter() + } + + /// Returns an iterator over the current result set. + /// + /// The returned iterator will be consumed either by the caller + /// or implicitly by the `ResultSet::drop`. This operation + /// will advance `self` to the next result set (if any). + /// + /// The following code describes the behavior: + /// + /// ```rust + /// # mysql::doctest_wrapper!(__result, { + /// # use mysql::*; + /// # use mysql::prelude::*; + /// # let pool = Pool::new(get_opts())?; + /// # let mut conn = pool.get_conn()?; + /// # conn.query_drop("CREATE TEMPORARY TABLE mysql.tbl(id INT NOT NULL PRIMARY KEY)")?; + /// + /// let mut query_result = conn.query_iter("\ + /// INSERT INTO mysql.tbl (id) VALUES (3, 4);\ + /// SELECT * FROM mysql.tbl; + /// UPDATE mysql.tbl SET id = id + 1;")?; + /// + /// // query_result is on the first result set at the moment + /// { + /// assert_eq!(query_result.affected_rows(), 2); + /// assert_eq!(query_result.last_insert_id(), Some(4)); + /// + /// let first_result_set = query_result.iter().unwrap(); + /// assert_eq!(first_result_set.affected_rows(), 2); + /// assert_eq!(first_result_set.last_insert_id(), Some(4)); + /// } + /// + /// // the first result set is now dropped, so query_result is on the second result set + /// { + /// assert_eq!(query_result.affected_rows(), 0); + /// assert_eq!(query_result.last_insert_id(), None); + /// + /// let mut second_result_set = query_result.iter().unwrap(); + /// + /// let first_row = second_result_set.next().unwrap().unwrap(); + /// assert_eq!(from_row::(first_row), 3_u8); + /// let second_row = second_result_set.next().unwrap().unwrap(); + /// assert_eq!(from_row::(second_row), 4_u8); + /// + /// assert!(second_result_set.next().is_none()); + /// + /// // second_result_set is consumed but still represents the second result set + /// assert_eq!(second_result_set.affected_rows(), 0); + /// } + /// + /// // the second result set is now dropped, so query_result is on the third result set + /// assert_eq!(query_result.affected_rows(), 2); /// - /// Subsequent call will return the next result set and so on. - pub fn current_set<'d>(&'d mut self) -> Option> { + /// // QueryResult::drop simply does the following: + /// while query_result.iter().is_some() {} + /// # }); + /// ``` + pub fn iter<'d>(&'d mut self) -> Option> { use SetIteratorState::*; if let OnBoundary | Done = &self.state { @@ -231,7 +290,7 @@ impl<'c, 't, 'tc, T: crate::prelude::Protocol> QueryResult<'c, 't, 'tc, T> { impl<'c, 't, 'tc, T: crate::prelude::Protocol> Drop for QueryResult<'c, 't, 'tc, T> { fn drop(&mut self) { - while self.current_set().is_some() {} + while self.iter().is_some() {} } } diff --git a/src/lib.rs b/src/lib.rs index ed1b85f..9176b41 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -103,6 +103,44 @@ //! # }); //! ``` //! +//! ## Crate Features +//! +//! * crate's features: +//! +//! * **native-tls** (enabled by default) – specifies `native-tls` as the TLS backend +//! (see the [SSL Support](#ssl-support) section) +//! * **rustls-tls** (disabled by default) – specifies `rustls` as the TLS backend +//! (see the [SSL Support](#ssl-support) section) +//! * **buffer-pool** (enabled by default) – enables buffer pooling +//! (see the [Buffer Pool](#buffer-pool) section) +//! +//! * external features enabled by default: +//! +//! * for the `flate2` crate (please consult `flate2` crate documentation for available features): +//! +//! * **flate2/zlib** (necessary) – `zlib` backend is chosed by default. +//! +//! * for the `mysql_common` crate (please consult `mysql_common` crate documentation for available features): +//! +//! * **mysql_common/bigdecimal03** – the `bigdecimal03` is enabled by default +//! * **mysql_common/rust_decimal** – the `rust_decimal` is enabled by default +//! * **mysql_common/time03** – the `time03` is enabled by default +//! * **mysql_common/uuid** – the `uuid` is enabled by default +//! * **mysql_common/frunk** – the `frunk` is enabled by default +//! +//! Please note, that you'll need to reenable external features if you are using `no-default-features = true`: +//! +//! ```toml +//! [dependencies] +//! # Lets say that we want to use the `rustls-tls` feature: +//! mysql = { version = "*", no-default-features = true, features = ["rustls-tls", "buffer-pool"] } +//! # Previous line disables default mysql features, +//! # so now we have to choose the flate2 backend (this is necessary), +//! # as well as the desired set of mysql_common features: +//! flate2 = { version = "*", no-default-features = true, features = ["zlib"] } +//! mysql_common = { version = "*", no-default-features = true, features = ["bigdecimal03", "time03", "uuid"]} +//! ``` +//! //! ## API Documentation //! //! Please refer to the [crate docs]. @@ -461,7 +499,7 @@ //! # }); //! ``` //! -//! ### `QueryResult` +//! ### [`QueryResult`] //! //! It's an iterator over rows of a query result with support of multi-result sets. It's intended //! for cases when you need full control during result set iteration. For other cases @@ -470,7 +508,7 @@ //! //! This iterator is lazy so it won't read the result from server until you iterate over it. //! MySql protocol is strictly sequential, so `Conn` will be mutably borrowed until the result -//! is fully consumed. +//! is fully consumed (please also look at [`QueryResult::iter`] docs). //! //! ```rust //! # #[macro_use] extern crate serde_derive; @@ -484,7 +522,7 @@ //! let mut result = conn.query_iter("SELECT 1, 2; SELECT 3, 3.14;")?; //! //! let mut sets = 0; -//! while let Some(result_set) = result.current_set() { +//! while let Some(result_set) = result.iter() { //! sets += 1; //! //! println!("Result set columns: {:?}", result_set.columns()); @@ -684,15 +722,14 @@ //! the following environment variables: //! //! * `RUST_MYSQL_BUFFER_POOL_CAP` (defaults to 128) – controls the pool capacity. Dropped buffer will -//! be immediately deallocated if the pool is full. -//! -//! **Note:** it might be the case, that you don't need the pooling (say you are using jemalloc). -//! It's possible to disable the pool by setting the `RUST_MYSQL_BUFFER_POOL_CAP` environment -//! variable to `0`. +//! be immediately deallocated if the pool is full. Set it to `0` to disable the pool at runtime. //! //! * `RUST_MYSQL_BUFFER_SIZE_CAP` (defaults to 4MiB) – controls the maximum capacity of a buffer //! stored in the pool. Capacity of a dropped buffer will be shrinked to this value when buffer -//! is returning to the pool. +//! is returned to the pool. +//! +//! To completely disable the pool (say you are using jemalloc) please remove the `buffer-pool` feature +//! from the set of default crate features (see the [Crate Features](#crate-features) section). //! //! ### `BinQuery` and `BatchQuery` traits. //! @@ -776,17 +813,9 @@ //! 1. Based on **native-tls** – this is the default option, that usually works without pitfalls //! (see the `native-tls` crate feature). //! 2. Based on **rustls** – TLS backend written in Rust. Please use the `rustls-tls` crate feature -//! to enable: -//! -//! ```toml -//! [dependencies] -//! mysql = { version = "*", no-default-features = true, features = ["rustls-tls"] } -//! # Please note, that the previous line disables default mysql features, -//! # so now we have to choose the flate2 backend (this is necessary): -//! flate2 = { version = "1.0", no-default-features = true, features = ["zlib"] } -//! ``` +//! to enable it (see the [Crate Features](#crate-features) section). //! -//! Please also note a few things about this backend: +//! Please also note a few things about **rustls**: //! //! * it will fail if you'll try to connect to the server by its IP address, hostname is required; //! * it, most likely, won't work on windows, at least with default server certs, generated by the @@ -801,8 +830,6 @@ #[cfg(feature = "nightly")] extern crate test; -use std::sync::Arc; - use mysql_common as myc; pub extern crate serde; pub extern crate serde_json; @@ -894,9 +921,6 @@ pub mod prelude { #[doc(inline)] pub use crate::myc::params; -static BUFFER_POOL: once_cell::sync::Lazy> = - once_cell::sync::Lazy::new(|| Default::default()); - #[doc(hidden)] #[macro_export] macro_rules! def_database_url {