use crate::config::AkitaConfig;
use crate::database_err;
use crate::driver::DriverType;
use crate::errors::AkitaError;
use oracle::Connector;
pub type OracleConnection = r2d2::PooledConnection<OracleConnectionManager>;
pub type OraclePool = r2d2::Pool<OracleConnectionManager>;
pub struct OracleConnectionManager {
connector: Connector,
}
impl OracleConnectionManager {
pub fn new(cfg: &AkitaConfig) -> Result<Self, AkitaError> {
let connector = cfg.try_into()?;
Ok(Self { connector })
}
pub fn with_tns(username: &str, password: &str, tns_name: &str) -> Self {
let connector = Connector::new(username, password, tns_name);
Self { connector }
}
pub fn with_easy_connect(username: &str, password: &str, easy_connect: &str) -> Self {
let connector = Connector::new(username, password, easy_connect);
Self { connector }
}
}
impl r2d2::ManageConnection for OracleConnectionManager {
type Connection = oracle::Connection;
type Error = oracle::Error;
fn connect(&self) -> std::result::Result<Self::Connection, Self::Error> {
self.connector.connect()
}
fn is_valid(&self, conn: &mut Self::Connection) -> Result<(), Self::Error> {
conn.query("SELECT 1 FROM DUAL", &[]).map(|_| ())
}
fn has_broken(&self, conn: &mut Self::Connection) -> bool {
conn.ping().is_err()
}
}
pub fn init_oracle_pool(cfg: AkitaConfig) -> Result<OraclePool, AkitaError> {
let manager = OracleConnectionManager::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 Oracle connection pool: {}", e)))?;
let conn = pool.get()?;
conn.query_row("SELECT 1 FROM DUAL", &[])?;
Ok(pool)
}
impl TryFrom<AkitaConfig> for Connector {
type Error = AkitaError;
fn try_from(v: AkitaConfig) -> Result<Self, Self::Error> {
Connector::try_from(&v)
}
}
impl TryFrom<&AkitaConfig> for Connector {
type Error = AkitaError;
fn try_from(cfg: &AkitaConfig) -> Result<Self, Self::Error> {
if cfg.get_platform()? != DriverType::Oracle {
return Err(database_err!(
"Database type mismatch: expected Oracle".to_string()
));
}
let username = cfg
.get_username()?
.ok_or_else(|| database_err!("Oracle username is required".to_string()))?;
let password = cfg
.get_password()?
.ok_or_else(|| database_err!("Oracle password is required".to_string()))?;
let mut connect_string = String::new();
if let Some(host) = cfg.get_hostname()? {
connect_string.push_str(&host);
if let Some(port) = cfg.get_port()? {
connect_string.push_str(&format!(":{}", port));
}
if let Some(service) = cfg.get_database()? {
connect_string.push_str(&format!("/{}", service));
}
} else {
if let Some(url) = cfg.get_url() {
if let Some(at_index) = url.find('@') {
connect_string = url[at_index + 1..].to_string();
} else {
return Err(database_err!(
"Oracle connection string is required".to_string()
));
}
} else {
return Err(database_err!(
"Oracle connection string is required".to_string()
));
}
}
Ok(Connector::new(username, password, connect_string))
}
}