mysql_async/lib.rs
1// Copyright (c) 2016 Anatoly Ikorsky
2//
3// Licensed under the Apache License, Version 2.0
4// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
5// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. All files in the project carrying such notice may not be copied,
7// modified, or distributed except according to those terms.
8
9//! Tokio based asynchronous MySql client library for The Rust Programming Language.
10//!
11//! # Installation
12//!
13//! The library is hosted on [crates.io](https://crates.io/crates/mysql_async/).
14//!
15//! ```toml
16//! [dependencies]
17//! mysql_async = "<desired version>"
18//! ```
19//!
20//! # Crate Features
21//!
22//! By default there are only two features enabled:
23//!
24//! * `flate2/zlib` — choosing flate2 backend is mandatory
25//! * `derive` — see ["Derive Macros" section in `mysql_common` docs][mysqlcommonderive]
26//!
27//! ## List Of Features
28//!
29//! * `minimal` – enables only necessary features (at the moment the only necessary feature
30//! is `flate2` backend). Enables:
31//!
32//! - `flate2/zlib"
33//!
34//! **Example:**
35//!
36//! ```toml
37//! [dependencies]
38//! mysql_async = { version = "*", default-features = false, features = ["minimal"]}
39//! ```
40//!
41//! * `minimal-rust` - same as `minimal` but with rust-based flate2 backend. Enables:
42//!
43//! - `flate2/rust_backend`
44//!
45//! * `default` – enables the following set of features:
46//!
47//! - `flate2/zlib`
48//! - `derive`
49//!
50//! * `default-rustls` – default set of features with TLS via `rustls/aws-lc-rs`
51//!
52//! * `default-rustls-ring` – default set of features with TLS via `rustls/ring`
53//!
54//! **Example:**
55//!
56//! ```toml
57//! [dependencies]
58//! mysql_async = { version = "*", default-features = false, features = ["default-rustls"] }
59//! ```
60//!
61//! * `native-tls-tls` – enables TLS via `native-tls`
62//!
63//! **Example:**
64//!
65//! ```toml
66//! [dependencies]
67//! mysql_async = { version = "*", default-features = false, features = ["minimal", "native-tls-tls"] }
68//!
69//! * `rustls-tls` - enables rustls TLS backend with no provider. You should enable one
70//! of existing providers using `aws-lc-rs` or `ring` features:
71//!
72//! **Example:**
73//!
74//! ```toml
75//! [dependencies]
76//! mysql_async = { version = "*", default-features = false, features = ["minimal-rust", "rustls-tls", "ring"] }
77//!
78//! * `tracing` – enables instrumentation via `tracing` package.
79//!
80//! Primary operations (`query`, `prepare`, `exec`) are instrumented at `INFO` level.
81//! Remaining operations, incl. `get_conn`, are instrumented at `DEBUG` level.
82//! Also at `DEBUG`, the SQL queries and parameters are added to the `query`, `prepare`
83//! and `exec` spans. Also some internal queries are instrumented at `TRACE` level.
84//!
85//! **Example:**
86//!
87//! ```toml
88//! [dependencies]
89//! mysql_async = { version = "*", features = ["tracing"] }
90//! ```
91//!
92//! * `binlog` - enables binlog-related functionality. Enables:
93//!
94//! - `mysql_common/binlog"
95//!
96//! ### Proxied features (see [`mysql_common`` fatures][myslqcommonfeatures])
97//!
98//! * `derive` – enables `mysql_common/derive` feature
99//! * `chrono` = enables `mysql_common/chrono` feature
100//! * `time` = enables `mysql_common/time` feature
101//! * `bigdecimal` = enables `mysql_common/bigdecimal` feature
102//! * `rust_decimal` = enables `mysql_common/rust_decimal` feature
103//! * `frunk` = enables `mysql_common/frunk` feature
104//!
105//! [myslqcommonfeatures]: https://github.com/blackbeam/rust_mysql_common#crate-features
106//! [mysqlcommonderive]: https://github.com/blackbeam/rust_mysql_common?tab=readme-ov-file#derive-macros
107//!
108//! # TLS/SSL Support
109//!
110//! SSL support comes in two flavors:
111//!
112//! 1. Based on native-tls – this is the default option, that usually works without pitfalls
113//! (see the `native-tls-tls` crate feature).
114//!
115//! 2. Based on rustls – TLS backend written in Rust (see the `rustls-tls` crate feature).
116//!
117//! Please also note a few things about rustls:
118//! - it will fail if you'll try to connect to the server by its IP address,
119//! hostname is required;
120//! - it, most likely, won't work on windows, at least with default server certs,
121//! generated by the MySql installer.
122//!
123//! # Connection URL parameters
124//!
125//! There is a set of url-parameters supported by the driver (see documentation on [`Opts`]).
126//!
127//! # Example
128//!
129//! ```rust
130//! # use mysql_async::{Result, test_misc::get_opts};
131//! use mysql_async::prelude::*;
132//! # use std::env;
133//!
134//! #[derive(Debug, PartialEq, Eq, Clone)]
135//! struct Payment {
136//! customer_id: i32,
137//! amount: i32,
138//! account_name: Option<String>,
139//! }
140//!
141//! #[tokio::main]
142//! async fn main() -> Result<()> {
143//! let payments = vec![
144//! Payment { customer_id: 1, amount: 2, account_name: None },
145//! Payment { customer_id: 3, amount: 4, account_name: Some("foo".into()) },
146//! Payment { customer_id: 5, amount: 6, account_name: None },
147//! Payment { customer_id: 7, amount: 8, account_name: None },
148//! Payment { customer_id: 9, amount: 10, account_name: Some("bar".into()) },
149//! ];
150//!
151//! let database_url = /* ... */
152//! # get_opts();
153//!
154//! let pool = mysql_async::Pool::new(database_url);
155//! let mut conn = pool.get_conn().await?;
156//!
157//! // Create a temporary table
158//! r"CREATE TEMPORARY TABLE payment (
159//! customer_id int not null,
160//! amount int not null,
161//! account_name text
162//! )".ignore(&mut conn).await?;
163//!
164//! // Save payments
165//! r"INSERT INTO payment (customer_id, amount, account_name)
166//! VALUES (:customer_id, :amount, :account_name)"
167//! .with(payments.iter().map(|payment| params! {
168//! "customer_id" => payment.customer_id,
169//! "amount" => payment.amount,
170//! "account_name" => payment.account_name.as_ref(),
171//! }))
172//! .batch(&mut conn)
173//! .await?;
174//!
175//! // Load payments from the database. Type inference will work here.
176//! let loaded_payments = "SELECT customer_id, amount, account_name FROM payment"
177//! .with(())
178//! .map(&mut conn, |(customer_id, amount, account_name)| Payment { customer_id, amount, account_name })
179//! .await?;
180//!
181//! // Dropped connection will go to the pool
182//! drop(conn);
183//!
184//! // The Pool must be disconnected explicitly because
185//! // it's an asynchronous operation.
186//! pool.disconnect().await?;
187//!
188//! assert_eq!(loaded_payments, payments);
189//!
190//! // the async fn returns Result, so
191//! Ok(())
192//! }
193//! ```
194//!
195//! # Pool
196//!
197//! The [`Pool`] structure is an asynchronous connection pool.
198//!
199//! Please note:
200//!
201//! * [`Pool`] is a smart pointer – each clone will point to the same pool instance.
202//! * [`Pool`] is `Send + Sync + 'static` – feel free to pass it around.
203//! * use [`Pool::disconnect`] to gracefuly close the pool.
204//! * ⚠️ [`Pool::new`] is lazy and won't assert server availability.
205//!
206//! # Transaction
207//!
208//! [`Conn::start_transaction`] is a wrapper, that starts with `START TRANSACTION`
209//! and ends with `COMMIT` or `ROLLBACK`.
210//!
211//! Dropped transaction will be implicitly rolled back if it wasn't explicitly
212//! committed or rolled back. Note that this behaviour will be triggered by a pool
213//! (on conn drop) or by the next query, i.e. may be delayed.
214//!
215//! API won't allow you to run nested transactions because some statements causes
216//! an implicit commit (`START TRANSACTION` is one of them), so this behavior
217//! is chosen as less error prone.
218//!
219//! # `Value`
220//!
221//! This enumeration represents the raw value of a MySql cell. Library offers conversion between
222//! `Value` and different rust types via `FromValue` trait described below.
223//!
224//! ## `FromValue` trait
225//!
226//! This trait is reexported from **mysql_common** create. Please refer to its
227//! [crate docs](https://docs.rs/mysql_common) for the list of supported conversions.
228//!
229//! Trait offers conversion in two flavours:
230//!
231//! * `from_value(Value) -> T` - convenient, but panicking conversion.
232//!
233//! Note, that for any variant of `Value` there exist a type, that fully covers its domain,
234//! i.e. for any variant of `Value` there exist `T: FromValue` such that `from_value` will never
235//! panic. This means, that if your database schema is known, than it's possible to write your
236//! application using only `from_value` with no fear of runtime panic.
237//!
238//! Also note, that some convertions may fail even though the type seem sufficient,
239//! e.g. in case of invalid dates (see [sql mode](https://dev.mysql.com/doc/refman/8.0/en/sql-mode.html)).
240//!
241//! * `from_value_opt(Value) -> Option<T>` - non-panicking, but less convenient conversion.
242//!
243//! This function is useful to probe conversion in cases, where source database schema
244//! is unknown.
245//!
246//! # MySql query protocols
247//!
248//! ## Text protocol
249//!
250//! MySql text protocol is implemented in the set of `Queryable::query*` methods
251//! and in the [`prelude::Query`] trait if query is [`prelude::AsQuery`].
252//! It's useful when your query doesn't have parameters.
253//!
254//! **Note:** All values of a text protocol result set will be encoded as strings by the server,
255//! so `from_value` conversion may lead to additional parsing costs.
256//!
257//! ## Binary protocol and prepared statements.
258//!
259//! MySql binary protocol is implemented in the set of `exec*` methods,
260//! defined on the [`prelude::Queryable`] trait and in the [`prelude::Query`]
261//! trait if query is [`QueryWithParams`]. Prepared statements is the only way to
262//! pass rust value to the MySql server. MySql uses `?` symbol as a parameter placeholder.
263//!
264//! **Note:** it's only possible to use parameters where a single MySql value
265//! is expected, i.e. you can't execute something like `SELECT ... WHERE id IN ?`
266//! with a vector as a parameter. You'll need to build a query that looks like
267//! `SELECT ... WHERE id IN (?, ?, ...)` and to pass each vector element as
268//! a parameter.
269//!
270//! # Named parameters
271//!
272//! MySql itself doesn't have named parameters support, so it's implemented on the client side.
273//! One should use `:name` as a placeholder syntax for a named parameter. Named parameters uses
274//! the following naming convention:
275//!
276//! * parameter name must start with either `_` or `a..z`
277//! * parameter name may continue with `_`, `a..z` and `0..9`
278//!
279//! **Note:** this rules mean that, say, the statment `SELECT :fooBar` will be translated
280//! to `SELECT ?Bar` so please be careful.
281//!
282//! Named parameters may be repeated within the statement, e.g `SELECT :foo, :foo` will require
283//! a single named parameter `foo` that will be repeated on the corresponding positions during
284//! statement execution.
285//!
286//! One should use the `params!` macro to build parameters for execution.
287//!
288//! **Note:** Positional and named parameters can't be mixed within the single statement.
289//!
290//! # Statements
291//!
292//! In MySql each prepared statement belongs to a particular connection and can't be executed
293//! on another connection. Trying to do so will lead to an error. The driver won't tie statement
294//! to its connection in any way, but one can look on to the connection id, contained
295//! in the [`Statement`] structure.
296//!
297//! # LOCAL INFILE Handlers
298//!
299//! **Warning:** You should be aware of [Security Considerations for LOAD DATA LOCAL][1].
300//!
301//! There are two flavors of LOCAL INFILE handlers – _global_ and _local_.
302//!
303//! I case of a LOCAL INFILE request from the server the driver will try to find a handler for it:
304//!
305//! 1. It'll try to use _local_ handler installed on the connection, if any;
306//! 2. It'll try to use _global_ handler, specified via [`OptsBuilder::local_infile_handler`],
307//! if any;
308//! 3. It will emit [`LocalInfileError::NoHandler`] if no handlers found.
309//!
310//! The purpose of a handler (_local_ or _global_) is to return [`InfileData`].
311//!
312//! ## _Global_ LOCAL INFILE handler
313//!
314//! See [`prelude::GlobalHandler`].
315//!
316//! Simply speaking the _global_ handler is an async function that takes a file name (as `&[u8]`)
317//! and returns `Result<InfileData>`.
318//!
319//! You can set it up using [`OptsBuilder::local_infile_handler`]. Server will use it if there is no
320//! _local_ handler installed for the connection. This handler might be called multiple times.
321//!
322//! Examles:
323//!
324//! 1. [`WhiteListFsHandler`] is a _global_ handler.
325//! 2. Every `T: Fn(&[u8]) -> BoxFuture<'static, Result<InfileData, LocalInfileError>>`
326//! is a _global_ handler.
327//!
328//! ## _Local_ LOCAL INFILE handler.
329//!
330//! Simply speaking the _local_ handler is a future, that returns `Result<InfileData>`.
331//!
332//! This is a one-time handler – it's consumed after use. You can set it up using
333//! [`Conn::set_infile_handler`]. This handler have priority over _global_ handler.
334//!
335//! Worth noting:
336//!
337//! 1. `impl Drop for Conn` will clear _local_ handler, i.e. handler will be removed when
338//! connection is returned to a `Pool`.
339//! 2. [`Conn::reset`] will clear _local_ handler.
340//!
341//! Example:
342//!
343//! ```rust
344//! # use mysql_async::{prelude::*, test_misc::get_opts, OptsBuilder, Result, Error};
345//! # use futures_util::future::FutureExt;
346//! # use futures_util::stream::{self, StreamExt};
347//! # use bytes::Bytes;
348//! # use std::env;
349//! # #[tokio::main]
350//! # async fn main() -> Result<()> {
351//! #
352//! # let database_url = get_opts();
353//! let pool = mysql_async::Pool::new(database_url);
354//!
355//! let mut conn = pool.get_conn().await?;
356//! "CREATE TEMPORARY TABLE tmp (id INT, val TEXT)".ignore(&mut conn).await?;
357//!
358//! // We are going to call `LOAD DATA LOCAL` so let's setup a one-time handler.
359//! conn.set_infile_handler(async move {
360//! // We need to return a stream of `io::Result<Bytes>`
361//! Ok(stream::iter([Bytes::from("1,a\r\n"), Bytes::from("2,b\r\n3,c")]).map(Ok).boxed())
362//! });
363//!
364//! let result = r#"LOAD DATA LOCAL INFILE 'whatever'
365//! INTO TABLE `tmp`
366//! FIELDS TERMINATED BY ',' ENCLOSED BY '\"'
367//! LINES TERMINATED BY '\r\n'"#.ignore(&mut conn).await;
368//!
369//! match result {
370//! Ok(()) => (),
371//! Err(Error::Server(ref err)) if err.code == 1148 => {
372//! // The used command is not allowed with this MySQL version
373//! return Ok(());
374//! },
375//! Err(Error::Server(ref err)) if err.code == 3948 => {
376//! // Loading local data is disabled;
377//! // this must be enabled on both the client and the server
378//! return Ok(());
379//! }
380//! e @ Err(_) => e.unwrap(),
381//! }
382//!
383//! // Now let's verify the result
384//! let result: Vec<(u32, String)> = conn.query("SELECT * FROM tmp ORDER BY id ASC").await?;
385//! assert_eq!(
386//! result,
387//! vec![(1, "a".into()), (2, "b".into()), (3, "c".into())]
388//! );
389//!
390//! drop(conn);
391//! pool.disconnect().await?;
392//! # Ok(())
393//! # }
394//! ```
395//!
396//! [1]: https://dev.mysql.com/doc/refman/8.0/en/load-data-local-security.html
397//!
398//! # Testing
399//!
400//! Tests uses followin environment variables:
401//! * `DATABASE_URL` – defaults to `mysql://root:password@127.0.0.1:3307/mysql`
402//! * `COMPRESS` – set to `1` or `true` to enable compression for tests
403//! * `SSL` – set to `1` or `true` to enable TLS for tests
404//!
405//! You can run a test server using doker. Please note that params related
406//! to max allowed packet, local-infile and binary logging are required
407//! to properly run tests (please refer to `azure-pipelines.yml`):
408//!
409//! ```sh
410//! docker run -d --name container \
411//! -v `pwd`:/root \
412//! -p 3307:3306 \
413//! -e MYSQL_ROOT_PASSWORD=password \
414//! mysql:8.0 \
415//! --max-allowed-packet=36700160 \
416//! --local-infile \
417//! --log-bin=mysql-bin \
418//! --log-slave-updates \
419//! --gtid_mode=ON \
420//! --enforce_gtid_consistency=ON \
421//! --server-id=1
422//! ```
423//!
424
425#![recursion_limit = "1024"]
426#![cfg_attr(feature = "nightly", feature(test))]
427
428#[cfg(feature = "nightly")]
429extern crate test;
430
431#[cfg(feature = "derive")]
432extern crate mysql_common;
433
434pub use mysql_common::{constants as consts, params};
435
436use std::sync::Arc;
437
438mod buffer_pool;
439
440#[macro_use]
441mod tracing_utils;
442
443#[macro_use]
444mod macros;
445mod conn;
446mod connection_like;
447/// Errors used in this crate
448mod error;
449mod io;
450mod local_infile_handler;
451mod opts;
452mod query;
453mod queryable;
454
455type BoxFuture<'a, T> = futures_core::future::BoxFuture<'a, Result<T>>;
456
457fn buffer_pool() -> &'static Arc<crate::buffer_pool::BufferPool> {
458 static BUFFER_POOL: std::sync::OnceLock<Arc<crate::buffer_pool::BufferPool>> =
459 std::sync::OnceLock::new();
460 BUFFER_POOL.get_or_init(Default::default)
461}
462
463#[cfg(feature = "binlog")]
464#[doc(inline)]
465pub use self::conn::binlog_stream::{request::BinlogStreamRequest, BinlogStream};
466
467#[doc(inline)]
468pub use self::conn::Conn;
469
470#[doc(inline)]
471pub use self::conn::pool::Pool;
472
473#[cfg(any(feature = "native-tls-tls", feature = "rustls-tls"))]
474#[doc(inline)]
475pub use self::error::tls::TlsError;
476
477#[doc(inline)]
478pub use self::error::{
479 DriverError, Error, IoError, LocalInfileError, ParseError, Result, ServerError, UrlError,
480};
481
482#[doc(inline)]
483pub use self::query::QueryWithParams;
484
485#[doc(inline)]
486pub use self::queryable::transaction::IsolationLevel;
487
488#[doc(inline)]
489#[cfg(any(feature = "rustls", feature = "native-tls-tls"))]
490pub use self::opts::ClientIdentity;
491
492#[doc(inline)]
493pub use self::opts::{
494 ChangeUserOpts, Opts, OptsBuilder, PoolConstraints, PoolOpts, SslOpts,
495 DEFAULT_INACTIVE_CONNECTION_TTL, DEFAULT_POOL_CONSTRAINTS, DEFAULT_STMT_CACHE_SIZE,
496 DEFAULT_TTL_CHECK_INTERVAL,
497};
498
499#[doc(inline)]
500pub use self::local_infile_handler::{builtin::WhiteListFsHandler, InfileData};
501
502#[doc(inline)]
503pub use mysql_common::packets::{
504 session_state_change::{
505 Gtids, Schema, SessionStateChange, SystemVariable, TransactionCharacteristics,
506 TransactionState, Unsupported,
507 },
508 Column, GnoInterval, OkPacket, SessionStateInfo, Sid,
509};
510
511#[cfg(feature = "binlog")]
512pub mod binlog {
513 #[doc(inline)]
514 pub use mysql_common::binlog::consts::*;
515
516 #[doc(inline)]
517 pub use mysql_common::binlog::{events, jsonb, jsondiff, row, value};
518}
519
520#[doc(inline)]
521pub use mysql_common::proto::codec::Compression;
522
523#[doc(inline)]
524pub use mysql_common::row::Row;
525
526#[doc(inline)]
527pub use mysql_common::params::Params;
528
529#[doc(inline)]
530pub use mysql_common::value::Value;
531
532#[doc(inline)]
533pub use mysql_common::row::convert::{from_row, from_row_opt, FromRowError};
534
535#[doc(inline)]
536pub use mysql_common::value::convert::{from_value, from_value_opt, FromValueError};
537
538#[doc(inline)]
539pub use mysql_common::value::json::{Deserialized, Serialized};
540
541#[doc(inline)]
542pub use self::queryable::query_result::{result_set_stream::ResultSetStream, QueryResult};
543
544#[doc(inline)]
545pub use self::queryable::transaction::{Transaction, TxOpts};
546
547#[doc(inline)]
548pub use self::queryable::{BinaryProtocol, TextProtocol};
549
550#[doc(inline)]
551pub use self::queryable::stmt::Statement;
552
553#[doc(inline)]
554pub use self::conn::pool::Metrics;
555
556/// Futures used in this crate
557pub mod futures {
558 pub use crate::conn::pool::futures::{DisconnectPool, GetConn};
559}
560
561/// Traits used in this crate
562pub mod prelude {
563 #[doc(inline)]
564 pub use crate::local_infile_handler::GlobalHandler;
565 #[doc(inline)]
566 pub use crate::query::AsQuery;
567 #[doc(inline)]
568 pub use crate::query::{BatchQuery, Query, WithParams};
569 #[doc(inline)]
570 pub use crate::queryable::Queryable;
571 #[doc(inline)]
572 pub use mysql_common::prelude::ColumnIndex;
573 #[doc(inline)]
574 pub use mysql_common::prelude::FromRow;
575 #[doc(inline)]
576 pub use mysql_common::prelude::{FromValue, ToValue};
577
578 /// Everything that is a statement.
579 ///
580 /// ```no_run
581 /// # use std::{borrow::Cow, sync::Arc};
582 /// # use mysql_async::{Statement, prelude::StatementLike};
583 /// fn type_is_a_stmt<T: StatementLike>() {}
584 ///
585 /// type_is_a_stmt::<Cow<'_, str>>();
586 /// type_is_a_stmt::<&'_ str>();
587 /// type_is_a_stmt::<String>();
588 /// type_is_a_stmt::<Box<str>>();
589 /// type_is_a_stmt::<Arc<str>>();
590 /// type_is_a_stmt::<Statement>();
591 ///
592 /// fn ref_to_a_clonable_stmt_is_also_a_stmt<T: StatementLike + Clone>() {
593 /// type_is_a_stmt::<&T>();
594 /// }
595 /// ```
596 pub trait StatementLike: crate::queryable::stmt::StatementLike {}
597 impl<T: crate::queryable::stmt::StatementLike> StatementLike for T {}
598
599 /// Everything that is a connection.
600 ///
601 /// Note that you could obtain a `'static` connection by giving away `Conn` or `Pool`.
602 pub trait ToConnection<'a, 't: 'a>: crate::connection_like::ToConnection<'a, 't> {}
603 // explicitly implemented because of rusdoc
604 impl<'a> ToConnection<'a, 'static> for &'a crate::Pool {}
605 impl ToConnection<'static, 'static> for crate::Pool {}
606 impl ToConnection<'static, 'static> for crate::Conn {}
607 impl<'a> ToConnection<'a, 'static> for &'a mut crate::Conn {}
608 impl<'a, 't> ToConnection<'a, 't> for &'a mut crate::Transaction<'t> {}
609
610 /// Trait for protocol markers [`crate::TextProtocol`] and [`crate::BinaryProtocol`].
611 pub trait Protocol: crate::queryable::Protocol {}
612 impl Protocol for crate::BinaryProtocol {}
613 impl Protocol for crate::TextProtocol {}
614
615 pub use mysql_common::params;
616}
617
618#[doc(hidden)]
619pub mod test_misc {
620 use std::env;
621 use std::sync::OnceLock;
622
623 use crate::opts::{Opts, OptsBuilder, SslOpts};
624
625 #[allow(dead_code)]
626 #[allow(unreachable_code)]
627 fn error_should_implement_send_and_sync(err: crate::Error) {
628 fn _dummy<T: Send + Sync + Unpin>(_: T) {}
629 _dummy(err);
630 }
631
632 pub fn get_opts() -> OptsBuilder {
633 static DATABASE_OPTS: OnceLock<Opts> = OnceLock::new();
634 let database_opts = DATABASE_OPTS.get_or_init(|| {
635 if let Ok(url) = env::var("DATABASE_URL") {
636 Opts::from_url(&url).expect("DATABASE_URL invalid")
637 } else {
638 Opts::from_url("mysql://root:password@localhost:3307/mysql").unwrap()
639 }
640 });
641
642 let mut builder = OptsBuilder::from_opts(database_opts.clone());
643 if test_ssl() {
644 let ssl_opts = SslOpts::default()
645 .with_danger_skip_domain_validation(true)
646 .with_danger_accept_invalid_certs(true);
647 builder = builder.prefer_socket(false).ssl_opts(ssl_opts);
648 }
649 if test_compression() {
650 builder = builder.compression(crate::Compression::default());
651 }
652 builder
653 }
654
655 pub fn test_compression() -> bool {
656 ["true", "1"].contains(&&*env::var("COMPRESS").unwrap_or_default())
657 }
658
659 pub fn test_ssl() -> bool {
660 ["true", "1"].contains(&&*env::var("SSL").unwrap_or_default())
661 }
662}