From 6371a5e9a111eb0f1453cdb1c5c6e49b458b9aa4 Mon Sep 17 00:00:00 2001 From: Anatoly Ikorsky Date: Fri, 24 Dec 2021 13:04:41 +0300 Subject: [PATCH 01/12] Bump version --- Cargo.toml | 2 +- tests/client.p12 | Bin 2453 -> 0 bytes 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 tests/client.p12 diff --git a/Cargo.toml b/Cargo.toml index 69bd3f40..d739b3f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mysql" -version = "21.0.2" +version = "22.0.0" authors = ["blackbeam"] description = "Mysql client library implemented in rust" license = "MIT/Apache-2.0" diff --git a/tests/client.p12 b/tests/client.p12 deleted file mode 100644 index f05b65a6ea2c843e5f627327a2246404f1fe04cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2453 zcmV;G32OE*f(elV0Ru3C30DRQDuzgg_YDCD0ic2jNCbijL@_XY_nhDe6@ z4FLxRpn?PFFoFZ*0s#Opf&<+K2`Yw2hW8Bt2LUh~1_~;MNQU(DO4q=qOAc;JuBVDmu}gP`sAnJJ2X$G%Box@!q(tE)T32)o)Yf`6VtB4 zfw7tiXvYOrN>%f`#deP?()xekgjWI8A-o-`GM$WpX2g23^ zQ>MJwi8(TSujTvG6oqmM)GFGTv5wx+JBA=_e*I6(aKQp4+BpDJ3QOV&n3NK)dX(RWxcsYa48R6qNq7-OUnF5vCM7*L$w>JNxsmH83<1^5g&L)p%V z3d#spBn0=sEYP$GKXGh2BFm&E`x3h${RRx|{fP(b#?i>YTW;1Bi{>iuAyGnYMQXG{ zn}1<Edx!OK`#~^mg$HMTHpYRXgVR!+QA7XM$VQ^5*WLlly+`Uk{c2%NXT@upUr_oTZ zgP%EKmW)B1j077NW3`&iH-Ar`!pKvCHI^3Hl2k}!1C`^Q^zCi*laigdf3hYL{aRuJ|rraB^t(0KveoH!tw+5~2}9K8)t&aS&61TR!$fgmJT{@Y2LZrFoFd^1_>&LNQU&G=*dH4?@Y@=0x>-C}E?Bv()C2>Q?PJs`)CWk{qMT zOwmxWiL0g^))r4kQx5EjAGB`jC3n0mXN`qe8cSzTiXu!P6F@-5{8|>A`xa}v8X=A9 zEp$i7F(*KFG59F%oasCZFblab&I7G9CMU3zCle3lrbNg{w}@j1p(@EZ zzuzW@QD71ouRi%twc(CoIFxPm4;v%$gD2wmB;+lGdhY|32HDaI&i64GzO$%t6OlZ>Ju`z)+)}vcpYLAiYOL|{%&cW1&Y=;sIEUnNb|*SapCcR zxa3(5kXxSzI*_EwTm$h>K{&)!jZ`IC&5+qn!!F2lw*}VT+ z=-M{4q>GeJjFLrcwDjlzJMQn|ApVn!00n-`p{kE`IZfvkdnZI|^ulEvkAJAvEJ=#l z`X6Y>RF8cgkF;_=6)d}wstoJii_?~e$i<;}PvV=@yE~zSCqm>@U8ZASH`7glZ(Z93 z!s~wGG-?j{dG5~$<5c#R0sqMzZJ^=x5Fn*JQJr2!&%KMQa?Fi9-$Y#$A4z3?tUVz}rOct7ao-hl?ra{%xF?zZm#Pq#DT{2QvvG{ZbJ+5YZU@kh)rH{XKT!j7H>C6h~lkdK9{p~OEK zo2<}@P6>Ms6?R}sp!P#2@DiP zZezcIXqJE`?{V5mOm>qS=Tr=So$c}(+g9~j?4B6$E&4|2eDX}0^t^NZtF9I0e^M~B zECU!s5n3wsV&zjKBs3=_urA?_uRt+!yIGG^FJcsnis8bM)*5%+1=rJ?!SxT1*@Bt9 z`I^;zWVW%DDDT;K9g2g{LqgPzv3+#q&tb87JU|V`DFh0GqB^7!uSQ)B>E=6B&GZ@f zKaFx(N3MLMKSXvx>{apL9ra}<0PXr1#exd7;uGMlLZXy)EwBDp)yh3tO9wsoc@bqj z0gM5(a~U}HUrx%($fHyJ@V}89L&XU9jj%faO%mAutIB1uG5%0vZJX1QbJ-*gROkMUCr4a}H*~(<;lp Tn4|;<%QNalXdEwc0s;sC;;4X+ From a1ce524fa4e0170f5ce81cb17b5294f79287881d Mon Sep 17 00:00:00 2001 From: Anatoly Ikorsky Date: Fri, 24 Dec 2021 13:14:03 +0300 Subject: [PATCH 02/12] Update the edition --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index d739b3f6..469b876f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ repository = "https://github.com/blackbeam/rust-mysql-simple" keywords = ["database", "sql"] exclude = ["tests/*", ".*", "Makefile"] categories = ["database"] -edition = "2018" +edition = "2021" build = "build.rs" [badges.azure-devops] From 1a63fe5fab5de0cbf9c8ed1c3ffc74f4e0a32275 Mon Sep 17 00:00:00 2001 From: Anatoly Ikorsky Date: Fri, 24 Dec 2021 13:46:44 +0300 Subject: [PATCH 03/12] Update MyTcpBuilder::connect to properly bind to address --- src/io/tcp.rs | 82 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 57 insertions(+), 25 deletions(-) diff --git a/src/io/tcp.rs b/src/io/tcp.rs index cba7259b..1ea21e5e 100644 --- a/src/io/tcp.rs +++ b/src/io/tcp.rs @@ -6,7 +6,7 @@ // option. All files in the project carrying such notice may not be copied, // modified, or distributed except according to those terms. -use socket2::{Domain, Socket, Type}; +use socket2::{Domain, SockAddr, Socket, Type}; use std::{ io, @@ -86,35 +86,67 @@ impl MyTcpBuilder { "could not connect to any address with specified bind address" }; let err = io::Error::new(io::ErrorKind::Other, err_msg); - address - .to_socket_addrs()? - .fold(Err(err), |prev, sock_addr| { - prev.or_else(|_| { - let domain = Domain::for_address(sock_addr); + + let addrs = address.to_socket_addrs()?.collect::>(); + + let socket = if let Some(bind_address) = bind_address { + let fold_fun = |prev, sock_addr: &SocketAddr| match prev { + Ok(socket) => Ok(socket), + Err(_) => { + let domain = Domain::for_address(*sock_addr); let socket = Socket::new(domain, Type::STREAM, None)?; - if let Some(bind_address) = bind_address { - if bind_address.is_ipv4() == sock_addr.is_ipv4() { - socket.bind(&bind_address.into())?; - } - } + socket.bind(&bind_address.into())?; if let Some(connect_timeout) = connect_timeout { - socket.connect_timeout(&sock_addr.into(), connect_timeout)?; + socket.connect_timeout(&SockAddr::from(*sock_addr), connect_timeout)?; } else { - socket.connect(&sock_addr.into())?; + socket.connect(&SockAddr::from(*sock_addr))?; } Ok(socket) - }) - }) - .and_then(|socket| { - socket.set_read_timeout(read_timeout)?; - socket.set_write_timeout(write_timeout)?; - if let Some(duration) = keepalive_time_ms { - let conf = socket2::TcpKeepalive::new() - .with_time(Duration::from_millis(duration as u64)); - socket.set_tcp_keepalive(&conf)?; } - socket.set_nodelay(nodelay)?; - Ok(TcpStream::from(socket)) - }) + }; + + if bind_address.is_ipv4() { + // client wants to bind to ipv4, so let's look for ipv4 addresses first + addrs + .iter() + .filter(|x| x.is_ipv4()) + .fold(Err(err), fold_fun) + .or_else(|e| addrs.iter().filter(|x| x.is_ipv6()).fold(Err(e), fold_fun)) + } else { + // client wants to bind to ipv6, so let's look for ipv6 addresses first + addrs + .iter() + .filter(|x| x.is_ipv6()) + .fold(Err(err), fold_fun) + .or_else(|e| addrs.iter().filter(|x| x.is_ipv4()).fold(Err(e), fold_fun)) + } + } else { + // no bind address + addrs + .into_iter() + .fold(Err(err), |prev, sock_addr| match prev { + Ok(socket) => Ok(socket), + Err(_) => { + let domain = Domain::for_address(sock_addr); + let socket = Socket::new(domain, Type::STREAM, None)?; + if let Some(connect_timeout) = connect_timeout { + socket.connect_timeout(&sock_addr.into(), connect_timeout)?; + } else { + socket.connect(&sock_addr.into())?; + } + Ok(socket) + } + }) + }?; + + socket.set_read_timeout(read_timeout)?; + socket.set_write_timeout(write_timeout)?; + if let Some(duration) = keepalive_time_ms { + let conf = + socket2::TcpKeepalive::new().with_time(Duration::from_millis(duration as u64)); + socket.set_tcp_keepalive(&conf)?; + } + socket.set_nodelay(nodelay)?; + Ok(TcpStream::from(socket)) } } From ed9f47fd6dabbfcf4f70bb2b557aab0021a494b3 Mon Sep 17 00:00:00 2001 From: Anatoly Ikorsky Date: Fri, 24 Dec 2021 13:56:10 +0300 Subject: [PATCH 04/12] Update docs --- README.md | 13 +++++++++---- src/conn/opts/mod.rs | 2 +- src/lib.rs | 5 +++++ 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 3b1dd656..0d57dad8 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,11 @@ Features: * MySql binary protocol support, i.e. support of prepared statements and binary result sets; * support of multi-result sets; * support of named parameters for prepared statements; + + Named parameters uses the following naming convention: + + * parameter name must start with either `_` or `a..z` and may continue with `_`, `a..z` and `0..9` + * optional per-connection cache of prepared statements; * support of MySql packets larger than 2^24; * support of Unix sockets and Windows named pipes; @@ -50,8 +55,8 @@ struct Payment { } let url = "mysql://root:password@localhost:3307/db_name"; -let opts = Opts::from_url(url)?; -let pool = Pool::new(opts)?; + +let pool = Pool::new(url)?; let mut conn = pool.get_conn()?; @@ -306,8 +311,8 @@ match unknown_val { println!("A double precision float value: {}", from_value::(val)) } val @ Value::Date(..) => { - use mysql::chrono::NaiveDateTime; - println!("A date value: {}", from_value::(val)) + use time::PrimitiveDateTime; + println!("A date value: {}", from_value::(val)) } val @ Value::Time(..) => { use std::time::Duration; diff --git a/src/conn/opts/mod.rs b/src/conn/opts/mod.rs index fb685e48..b9847b6e 100644 --- a/src/conn/opts/mod.rs +++ b/src/conn/opts/mod.rs @@ -481,7 +481,7 @@ impl OptsBuilder { /// - stmt_cache_size = Number of prepared statements cached on the client side (per connection) /// - secure_auth = Disable `mysql_old_password` auth plugin /// - /// Login .cnf file parsing lib https://github.com/rjcortese/myloginrs returns a HashMap for client configs + /// Login .cnf file parsing lib returns a HashMap for client configs /// /// **Note:** You do **not** have to use myloginrs lib. pub fn from_hash_map(mut self, client: &HashMap) -> Result { diff --git a/src/lib.rs b/src/lib.rs index fc77af87..be1cb09e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,6 +19,11 @@ //! * MySql binary protocol support, i.e. support of prepared statements and binary result sets; //! * support of multi-result sets; //! * support of named parameters for prepared statements; +//! +//! Named parameters uses the following naming convention: +//! +//! * parameter name must start with either `_` or `a..z` and may continue with `_`, `a..z` and `0..9` +//! //! * optional per-connection cache of prepared statements; //! * support of MySql packets larger than 2^24; //! * support of Unix sockets and Windows named pipes; From 25132a3c0be4729180ef6227ca0bf45a0573786e Mon Sep 17 00:00:00 2001 From: Anatoly Ikorsky Date: Sat, 25 Dec 2021 11:45:02 +0300 Subject: [PATCH 05/12] Redefine QueryResult::next_set as QueryResult::current_set Removes `Result` from the return type. --- README.md | 2 +- src/conn/mod.rs | 17 +++++++++++++++-- src/conn/query.rs | 4 ++-- src/conn/query_result.rs | 12 +++++++----- src/lib.rs | 3 +-- 5 files changed, 26 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 0d57dad8..f91ac2a0 100644 --- a/README.md +++ b/README.md @@ -460,7 +460,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.next_set() { +while let Some(result_set) = result.current_set() { let result_set = result_set?; sets += 1; diff --git a/src/conn/mod.rs b/src/conn/mod.rs index 8592cc1f..cc2899e4 100644 --- a/src/conn/mod.rs +++ b/src/conn/mod.rs @@ -1703,6 +1703,19 @@ mod test { ) .unwrap(); conn.exec_drop("SELECT * FROM TEST_TABLE", ()).unwrap(); + + let mut query_result = conn + .query_iter( + r" + SELECT * FROM TEST_TABLE; + INSERT INTO TEST_TABLE (name) VALUES ('one'); + DO 0;", + ) + .unwrap(); + + while let Some(result) = query_result.current_set() { + result.affected_rows(); + } } #[test] @@ -1739,9 +1752,9 @@ 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.next_set() { + while let Some(result_set) = result.current_set() { i += 1; - for row in result_set.unwrap() { + for row in result_set { match i { 1 => assert_eq!(row.unwrap().unwrap(), vec![Bytes(b"1".to_vec())]), 2 => assert_eq!(row.unwrap().unwrap(), vec![Bytes(b"2".to_vec())]), diff --git a/src/conn/query.rs b/src/conn/query.rs index b4a64984..4911ae99 100644 --- a/src/conn/query.rs +++ b/src/conn/query.rs @@ -384,8 +384,8 @@ 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.next_set() { - for row in result_set? { + while let Some(result_set) = query_result.current_set() { + for row in result_set { row?; } } diff --git a/src/conn/query_result.rs b/src/conn/query_result.rs index 9bd35ac7..341bec2f 100644 --- a/src/conn/query_result.rs +++ b/src/conn/query_result.rs @@ -100,7 +100,7 @@ impl From, OkPacket<'static>>> for SetIteratorState { /// Response to a query or statement execution. /// /// It is an iterator: -/// * over result sets (via `Self::next_set`) +/// * over result sets (via `Self::current_set`) /// * over rows of a current result set (via `Iterator` impl) #[derive(Debug)] pub struct QueryResult<'c, 't, 'tc, T: crate::prelude::Protocol> { @@ -153,7 +153,9 @@ impl<'c, 't, 'tc, T: crate::prelude::Protocol> QueryResult<'c, 't, 'tc, T> { } /// Returns an iterator over the current result set. - pub fn next_set<'d>(&'d mut self) -> Option>> { + /// + /// Subsequent call will return the next result set and so on. + pub fn current_set<'d>(&'d mut self) -> Option> { use SetIteratorState::*; if let OnBoundary | Done = &self.state { @@ -164,10 +166,10 @@ impl<'c, 't, 'tc, T: crate::prelude::Protocol> QueryResult<'c, 't, 'tc, T> { None } else { - Some(Ok(ResultSet { + Some(ResultSet { set_index: self.set_index, inner: self, - })) + }) } } @@ -229,7 +231,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.next_set().is_some() {} + while self.current_set().is_some() {} } } diff --git a/src/lib.rs b/src/lib.rs index be1cb09e..17489f5c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -488,8 +488,7 @@ //! let mut result = conn.query_iter("SELECT 1, 2; SELECT 3, 3.14;")?; //! //! let mut sets = 0; -//! while let Some(result_set) = result.next_set() { -//! let result_set = result_set?; +//! while let Some(result_set) = result.current_set() { //! sets += 1; //! //! println!("Result set columns: {:?}", result_set.columns()); From d4d781e98431308a3751a5cbe204ee94c9c58181 Mon Sep 17 00:00:00 2001 From: Anatoly Ikorsky Date: Tue, 28 Dec 2021 09:06:31 +0300 Subject: [PATCH 06/12] Make BufferPool lock free --- Cargo.toml | 1 + src/buffer_pool.rs | 92 ++++++++++++++++++++++++---------------------- 2 files changed, 49 insertions(+), 44 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 469b876f..b95a6a9b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,7 @@ time = "0.3" [dependencies] bufstream = "~0.1" bytes = "1.0.1" +crossbeam = "0.8.1" io-enum = "1.0.0" flate2 = { version = "1.0", default-features = false } lru = "0.7" diff --git a/src/buffer_pool.rs b/src/buffer_pool.rs index 54b427ee..8d9f2d07 100644 --- a/src/buffer_pool.rs +++ b/src/buffer_pool.rs @@ -6,63 +6,65 @@ // option. All files in the project carrying such notice may not be copied, // modified, or distributed except according to those terms. -use std::{ - mem::replace, - ops::Deref, - sync::{Arc, Mutex}, -}; +use crossbeam::queue::ArrayQueue; + +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; #[derive(Debug)] -pub struct BufferPool { - pool_cap: usize, +struct Inner { buffer_cap: usize, - pool: Mutex>>, + pool: ArrayQueue>, } -impl BufferPool { - pub fn new() -> Self { - let pool_cap = std::env::var("MYSQL_BUFFER_POOL_CAP") - .ok() - .and_then(|x| x.parse().ok()) - .unwrap_or(128_usize); - - let buffer_cap = std::env::var("MYSQL_BUFFER_SIZE_CAP") - .ok() - .and_then(|x| x.parse().ok()) - .unwrap_or(4 * 1024 * 1024); - - Self { - pool: Default::default(), - pool_cap, - buffer_cap, - } - } - - pub fn get(self: &Arc) -> PooledBuf { - let mut buf = self.pool.lock().unwrap().pop().unwrap_or_default(); +impl Inner { + fn get(self: &Arc) -> PooledBuf { + let mut buf = self.pool.pop().unwrap_or_default(); // SAFETY: // 1. OK – 0 is always within capacity // 2. OK - nothing to initialize unsafe { buf.set_len(0) } - PooledBuf(buf, self.clone()) + PooledBuf(buf, Some(self.clone())) } - fn put(self: &Arc, mut buf: Vec) { - if buf.len() > self.buffer_cap { - // TODO: until `Vec::shrink_to` stabilization + fn put(&self, mut buf: Vec) { + buf.shrink_to(self.buffer_cap); + let _ = self.pool.push(buf); + } +} - // SAFETY: - // 1. OK – new_len <= capacity - // 2. OK - 0..new_len is initialized - unsafe { buf.set_len(self.buffer_cap) } - buf.shrink_to_fit(); - } +/// Smart pointer to a buffer pool. +#[derive(Debug, Clone)] +pub struct BufferPool(Option>); + +impl BufferPool { + pub fn new() -> Self { + let pool_cap = std::env::var("RUST_MYSQL_BUFFER_POOL_CAP") + .ok() + .and_then(|x| x.parse().ok()) + .unwrap_or(DEFAULT_MYSQL_BUFFER_POOL_CAP); - let mut pool = self.pool.lock().unwrap(); - if pool.len() < self.pool_cap { - pool.push(buf); + let buffer_cap = std::env::var("RUST_MYSQL_BUFFER_SIZE_CAP") + .ok() + .and_then(|x| x.parse().ok()) + .unwrap_or(DEFAULT_MYSQL_BUFFER_SIZE_CAP); + + Self((pool_cap > 0).then(|| { + Arc::new(Inner { + buffer_cap, + pool: ArrayQueue::new(pool_cap), + }) + })) + } + + pub fn get(self: &Arc) -> PooledBuf { + match self.0 { + Some(ref inner) => inner.get(), + None => PooledBuf(Vec::new(), None), } } } @@ -74,7 +76,7 @@ impl Default for BufferPool { } #[derive(Debug)] -pub struct PooledBuf(Vec, Arc); +pub struct PooledBuf(Vec, Option>); impl AsMut> for PooledBuf { fn as_mut(&mut self) -> &mut Vec { @@ -92,6 +94,8 @@ impl Deref for PooledBuf { impl Drop for PooledBuf { fn drop(&mut self) { - self.1.put(replace(&mut self.0, vec![])) + if let Some(ref inner) = self.1 { + inner.put(replace(&mut self.0, vec![])); + } } } From b120cbc4a9ef1c761c2c3012905cebfc21dee786 Mon Sep 17 00:00:00 2001 From: Anatoly Ikorsky Date: Tue, 28 Dec 2021 09:36:54 +0300 Subject: [PATCH 07/12] docs: Add the "Buffer pool" section --- src/lib.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 17489f5c..7762f592 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,6 +25,7 @@ //! * parameter name must start with either `_` or `a..z` and may continue with `_`, `a..z` and `0..9` //! //! * optional per-connection cache of prepared statements; +//! * buffer pool (see the [Buffer Pool](#buffer-pool) section); //! * support of MySql packets larger than 2^24; //! * support of Unix sockets and Windows named pipes; //! * support of custom LOCAL INFILE handlers; @@ -677,6 +678,23 @@ //! # }); //! ``` //! +//! ### Buffer pool +//! +//! Crate uses the global lock-free buffer pool for the purpose of IO and data serialization/deserialization, +//! that helps to avoid allocations for basic scenarios. You can control it's characteristics using +//! 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`. +//! +//! * `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. +//! //! ### `BinQuery` and `BatchQuery` traits. //! //! `BinQuery` and `BatchQuery` traits covers the set of `Queryable::exec*` methods from From 3a5adc117ee8caa28c77db9695212af6fce4f00f Mon Sep 17 00:00:00 2001 From: Anatoly Ikorsky Date: Tue, 28 Dec 2021 09:37:50 +0300 Subject: [PATCH 08/12] docs: Few improvements --- src/lib.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 7762f592..e4e02a7e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,13 +18,8 @@ //! * MySql text protocol support, i.e. support of simple text queries and text result sets; //! * MySql binary protocol support, i.e. support of prepared statements and binary result sets; //! * support of multi-result sets; -//! * support of named parameters for prepared statements; -//! -//! Named parameters uses the following naming convention: -//! -//! * parameter name must start with either `_` or `a..z` and may continue with `_`, `a..z` and `0..9` -//! -//! * optional per-connection cache of prepared statements; +//! * support of named parameters for prepared statements (see the [Named Parameters](#named-parameters) section); +//! * optional per-connection cache of prepared statements (see the [Statement Cache](#statement-cache) section); //! * buffer pool (see the [Buffer Pool](#buffer-pool) section); //! * support of MySql packets larger than 2^24; //! * support of Unix sockets and Windows named pipes; @@ -646,13 +641,17 @@ //! ### Named parameters //! //! MySql itself doesn't have named parameters support, so it's implemented on the client side. -//! One should use `:name` as a placeholder syntax for a named parameter. +//! One should use `:name` as a placeholder syntax for a named parameter. Named parameters uses +//! the following naming convention: +//! +//! * parameter name must start with either `_` or `a..z` +//! * parameter name may continue with `_`, `a..z` and `0..9` //! //! Named parameters may be repeated within the statement, e.g `SELECT :foo, :foo` will require //! a single named parameter `foo` that will be repeated on the corresponding positions during //! statement execution. //! -//! One should use the `params!` macro to build a parameters for execution. +//! One should use the `params!` macro to build parameters for execution. //! //! **Note:** Positional and named parameters can't be mixed within the single statement. //! From 9b200ae21b1116341130e6fab625256fcea019ed Mon Sep 17 00:00:00 2001 From: Anatoly Ikorsky Date: Tue, 28 Dec 2021 09:41:12 +0300 Subject: [PATCH 09/12] Rebuild README.md --- README.md | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index f91ac2a0..8936cd4c 100644 --- a/README.md +++ b/README.md @@ -17,13 +17,9 @@ Features: * MySql text protocol support, i.e. support of simple text queries and text result sets; * MySql binary protocol support, i.e. support of prepared statements and binary result sets; * support of multi-result sets; -* support of named parameters for prepared statements; - - Named parameters uses the following naming convention: - - * parameter name must start with either `_` or `a..z` and may continue with `_`, `a..z` and `0..9` - -* optional per-connection cache of prepared statements; +* support of named parameters for prepared statements (see the [Named Parameters](#named-parameters) section); +* optional per-connection cache of prepared statements (see the [Statement Cache](#statement-cache) section); +* buffer pool (see the [Buffer Pool](#buffer-pool) section); * support of MySql packets larger than 2^24; * support of Unix sockets and Windows named pipes; * support of custom LOCAL INFILE handlers; @@ -461,7 +457,6 @@ 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() { - let result_set = result_set?; sets += 1; println!("Result set columns: {:?}", result_set.columns()); @@ -603,13 +598,17 @@ Statement cache is completely disabled if `stmt_cache_size` is zero. #### Named parameters MySql itself doesn't have named parameters support, so it's implemented on the client side. -One should use `:name` as a placeholder syntax for a named parameter. +One should use `:name` as a placeholder syntax for a named parameter. Named parameters uses +the following naming convention: + +* parameter name must start with either `_` or `a..z` +* parameter name may continue with `_`, `a..z` and `0..9` Named parameters may be repeated within the statement, e.g `SELECT :foo, :foo` will require a single named parameter `foo` that will be repeated on the corresponding positions during statement execution. -One should use the `params!` macro to build a parameters for execution. +One should use the `params!` macro to build parameters for execution. **Note:** Positional and named parameters can't be mixed within the single statement. @@ -631,6 +630,23 @@ assert_eq!((foo, 13, foo), val_42); assert_eq!((13, foo, 13), val_13); ``` +#### Buffer pool + +Crate uses the global lock-free buffer pool for the purpose of IO and data serialization/deserialization, +that helps to avoid allocations for basic scenarios. You can control it's characteristics using +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`. + +* `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. + #### `BinQuery` and `BatchQuery` traits. `BinQuery` and `BatchQuery` traits covers the set of `Queryable::exec*` methods from From c7a3b3b4a85972e4f2d59518764736fa3e40da68 Mon Sep 17 00:00:00 2001 From: Anatoly Ikorsky Date: Tue, 28 Dec 2021 10:45:35 +0300 Subject: [PATCH 10/12] CI: Connect to `localhost` instead of `127.0.0.1` --- azure-pipelines.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 064bf2cd..5a6a5c06 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -52,7 +52,7 @@ jobs: SSL=false COMPRESS=false cargo test --no-default-features --features flate2/zlib,mysql_common/time03 env: RUST_BACKTRACE: 1 - DATABASE_URL: mysql://root:root@127.0.0.1:3306/mysql + DATABASE_URL: mysql://root:root@localhost:3306/mysql displayName: Run tests - job: "TestBasicMacOs" @@ -98,7 +98,7 @@ jobs: SSL=false COMPRESS=false cargo test --no-default-features --features flate2/zlib,mysql_common/time03 env: RUST_BACKTRACE: 1 - DATABASE_URL: mysql://root@127.0.0.1/mysql + DATABASE_URL: mysql://root@localhost/mysql displayName: Run tests - job: "TestBasicWindows" @@ -149,7 +149,7 @@ jobs: SSL=false COMPRESS=false cargo test --no-default-features --features flate2/zlib,mysql_common/time03 env: RUST_BACKTRACE: 1 - DATABASE_URL: mysql://root:password@127.0.0.1/mysql + DATABASE_URL: mysql://root:password@localhost/mysql displayName: Run tests - job: "TestMySql" @@ -186,7 +186,7 @@ jobs: docker exec container bash -l -c "curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain stable" displayName: Install Rust in docker - bash: | - if [[ "5.6" != "$(DB_VERSION)" ]]; then SSL=true; else DATABASE_URL="mysql://root2:password@127.0.0.1/mysql?secure_auth=false"; fi + if [[ "5.6" != "$(DB_VERSION)" ]]; then SSL=true; else DATABASE_URL="mysql://root2:password@localhost/mysql?secure_auth=false"; fi docker exec container bash -l -c "cd \$HOME && DATABASE_URL=$DATABASE_URL cargo test" docker exec container bash -l -c "cd \$HOME && DATABASE_URL=$DATABASE_URL COMPRESS=true cargo test" docker exec container bash -l -c "cd \$HOME && DATABASE_URL=$DATABASE_URL SSL=$SSL cargo test" @@ -199,7 +199,7 @@ jobs: docker exec container bash -l -c "cd \$HOME && DATABASE_URL=$DATABASE_URL SSL=false COMPRESS=false cargo test --no-default-features --features flate2/zlib,mysql_common/time03" env: RUST_BACKTRACE: 1 - DATABASE_URL: mysql://root:password@127.0.0.1/mysql + DATABASE_URL: mysql://root:password@localhost/mysql displayName: Run tests in Docker - job: "TestMariaDb" @@ -267,5 +267,5 @@ jobs: docker exec container bash -l -c "cd \$HOME && DATABASE_URL=$DATABASE_URL SSL=false COMPRESS=false cargo test --no-default-features --features flate2/zlib,mysql_common/time03" env: RUST_BACKTRACE: 1 - DATABASE_URL: mysql://root:password@127.0.0.1/mysql + DATABASE_URL: mysql://root:password@localhost/mysql displayName: Run tests in Docker From 7825610ac8659ac5a01ff8abf22c34efb29e1498 Mon Sep 17 00:00:00 2001 From: Anatoly Ikorsky Date: Tue, 28 Dec 2021 16:56:59 +0300 Subject: [PATCH 11/12] docs: Add the "SSL Support" section --- Cargo.toml | 3 +++ README.md | 25 ++++++++++++++++++++++++- src/lib.rs | 25 ++++++++++++++++++++++++- 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b95a6a9b..269f44a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,10 @@ debug = true [features] default = [ "native-tls", + + # It is necessary to choose one of `flate2` backends. "flate2/zlib", + "mysql_common/bigdecimal03", "mysql_common/rust_decimal", "mysql_common/time03", diff --git a/README.md b/README.md index 8936cd4c..85886b24 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ This crate offers: Features: * macOS, Windows and Linux support; -* TLS support via **nativetls** create; +* TLS support via **nativetls** or **rustls** (see the [SSL Support](#ssl-support) section); * MySql text protocol support, i.e. support of simple text queries and text result sets; * MySql binary protocol support, i.e. support of prepared statements and binary result sets; * support of multi-result sets; @@ -718,6 +718,29 @@ These methods will consume only the first result set, other result sets will be The trait also defines the `exec_batch` function, which is a helper for batch statement execution. +### SSL Support + +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"] } + ``` + + Please also note a few things about this backend: + + * 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 + MySql installer. + [crate docs]: https://docs.rs/mysql [mysql_common docs]: https://docs.rs/mysql_common [max_prepared_stmt_count]: https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_max_prepared_stmt_count diff --git a/src/lib.rs b/src/lib.rs index e4e02a7e..d31da3cb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,7 +14,7 @@ //! Features: //! //! * macOS, Windows and Linux support; -//! * TLS support via **nativetls** create; +//! * TLS support via **nativetls** or **rustls** (see the [SSL Support](#ssl-support) section); //! * MySql text protocol support, i.e. support of simple text queries and text result sets; //! * MySql binary protocol support, i.e. support of prepared statements and binary result sets; //! * support of multi-result sets; @@ -769,6 +769,29 @@ //! The trait also defines the `exec_batch` function, which is a helper for batch statement //! execution. //! +//! ## SSL Support +//! +//! 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"] } +//! ``` +//! +//! Please also note a few things about this backend: +//! +//! * 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 +//! MySql installer. +//! //! [crate docs]: https://docs.rs/mysql //! [mysql_common docs]: https://docs.rs/mysql_common //! [max_prepared_stmt_count]: https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_max_prepared_stmt_count From 2dce4ae310233bc68ab235d8f457d8f37460f88b Mon Sep 17 00:00:00 2001 From: Anatoly Ikorsky Date: Tue, 28 Dec 2021 17:19:50 +0300 Subject: [PATCH 12/12] Remove crate_* attributes --- src/lib.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d31da3cb..ed1b85f3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -797,9 +797,6 @@ //! [max_prepared_stmt_count]: https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_max_prepared_stmt_count //! -#![crate_name = "mysql"] -#![crate_type = "rlib"] -#![crate_type = "dylib"] #![cfg_attr(feature = "nightly", feature(test))] #[cfg(feature = "nightly")] extern crate test;