use crate::config::AkitaConfig;
use crate::database_err;
use crate::errors::AkitaError;
use mysql::prelude::Queryable;
use mysql::{Conn, Error, Opts};
pub type MysqlPool = r2d2::Pool<MysqlConnectionManager>;
pub type MysqlConnection = r2d2::PooledConnection<MysqlConnectionManager>;
#[allow(unused)]
#[derive(Clone, Debug)]
pub struct MysqlConnectionManager {
params: Opts,
cfg: AkitaConfig,
}
impl MysqlConnectionManager {
pub fn new(cfg: &AkitaConfig) -> Result<Self, AkitaError> {
let connection_string = cfg.get_connection_string()?;
let opts = mysql::Opts::from_url(&connection_string)
.map_err(|e| database_err!(format!("Invalid MySQL URL: {}", e)))?;
Ok(Self {
params: Opts::from(opts),
cfg: cfg.clone(),
})
}
}
impl r2d2::ManageConnection for MysqlConnectionManager {
type Connection = Conn;
type Error = Error;
fn connect(&self) -> std::result::Result<Conn, Error> {
Conn::new(self.params.clone())
}
fn is_valid(&self, conn: &mut Conn) -> std::result::Result<(), Error> {
match conn.ping() {
Ok(_) => Ok(()),
Err(_) => {
conn.query_drop("SELECT 1")
}
}
}
fn has_broken(&self, conn: &mut Conn) -> bool {
!conn.ping().is_ok() || self.is_valid(conn).is_err()
}
}
pub fn init_mysql_pool(cfg: AkitaConfig) -> Result<MysqlPool, AkitaError> {
let manager = MysqlConnectionManager::new(&cfg)?;
let pool = r2d2::Pool::builder()
.connection_timeout(cfg.get_connection_timeout())
.min_idle(cfg.get_min_idle().into())
.max_size(cfg.get_max_size())
.idle_timeout(Some(cfg.get_idle_timeout()))
.max_lifetime(Some(cfg.get_max_lifetime()))
.test_on_check_out(cfg.get_test_on_check_out())
.build(manager)
.map_err(|e| database_err!(format!("Failed to create MySQL connection pool: {}", e)))?;
let mut conn = pool.get()?;
conn.query_drop("SELECT 1")?;
Ok(pool)
}
#[cfg(feature = "mysql-sync")]
impl From<&AkitaConfig> for mysql::OptsBuilder {
fn from(config: &AkitaConfig) -> Self {
let mut opts = mysql::OptsBuilder::new();
if let Ok(host) = config.get_hostname() {
opts = opts.ip_or_hostname(host);
}
if let Ok(port) = config.get_port() {
opts = opts.tcp_port(port.unwrap_or(3306));
}
if let Ok(db) = config.get_database() {
opts = opts.db_name(db);
}
if let Ok(username) = config.get_username() {
opts = opts.user(username);
}
if let Ok(password) = config.get_password() {
opts = opts.pass(password);
}
if let Ok(params) = config.get_params() {
for (key, value) in params {
match key.as_str() {
"useSSL" if value == "false" => {
opts = opts.ssl_opts(None);
}
_ => {}
}
}
}
opts
}
}
#[cfg(feature = "mysql-sync")]
impl From<AkitaConfig> for mysql::OptsBuilder {
fn from(config: AkitaConfig) -> Self {
mysql::OptsBuilder::from(&config)
}
}