use common::set_handle_attribute;
use libc::{c_int, c_uint, c_void, size_t};
use oci_bindings::{
AttributeType, CredentialsType, EnvironmentMode, HandleType, OCIEnv, OCIEnvCreate, OCIError,
OCIHandleAlloc, OCIHandleFree, OCIServer, OCIServerAttach, OCIServerDetach, OCISession,
OCISessionBegin, OCISessionEnd, OCISvcCtx, ReturnCode,
};
use oci_error::{get_error, OciError};
use statement::Statement;
use std::ptr;
#[derive(Debug)]
pub struct Connection {
environment: *mut OCIEnv,
server: *mut OCIServer,
error: *mut OCIError,
service: *mut OCISvcCtx,
session: *mut OCISession,
}
impl Connection {
pub fn new(
connection_str: &str,
user_name: &str,
password: &str,
) -> Result<Connection, OciError> {
let environment = create_environment_handle()?;
let server = create_server_handle(environment)?;
let error = create_error_handle(environment)?;
let service = create_service_handle(environment)?;
let session = create_session_handle(environment)?;
connect_to_database(server, connection_str, error)?;
set_server_in_service(service, server, error)?;
set_user_name_in_session(session, user_name, error)?;
set_password_in_session(session, password, error)?;
start_session(service, session, error)?;
set_session_in_service(service, session, error)?;
Ok(Connection {
environment: environment,
server: server,
error: error,
service: service,
session: session,
})
}
pub fn create_prepared_statement(&self, sql: &str) -> Result<Statement, OciError> {
Statement::new(self, sql)
}
pub(crate) fn error(&self) -> *mut OCIError {
self.error
}
pub(crate) fn error_as_void(&self) -> *mut c_void {
self.error as *mut c_void
}
pub(crate) fn service(&self) -> *mut OCISvcCtx {
self.service
}
}
impl Drop for Connection {
fn drop(&mut self) {
let session_end_result = unsafe {
OCISessionEnd(
self.service,
self.error,
self.session,
EnvironmentMode::Default.into(),
)
};
match session_end_result.into() {
ReturnCode::Success => (),
_ => println!("Could not end user session"), }
let disconnect_result =
unsafe { OCIServerDetach(self.server, self.error, EnvironmentMode::Default.into()) };
match disconnect_result.into() {
ReturnCode::Success => (),
_ => println!("Could not disconnect"), }
let free_result = unsafe {
OCIHandleFree(
self.environment as *mut c_void,
HandleType::Environment.into(),
)
};
match free_result.into() {
ReturnCode::Success => (),
_ => panic!("Could not free the handles in Connection"),
}
}
}
fn create_environment_handle() -> Result<*mut OCIEnv, OciError> {
let env: *mut OCIEnv = ptr::null_mut();
let mode = EnvironmentMode::Threaded.into();
let xtramem_sz: size_t = 0;
let null_ptr = ptr::null();
let env_result = unsafe {
OCIEnvCreate(
&env, mode, null_ptr, null_ptr, null_ptr, null_ptr, xtramem_sz, null_ptr,
)
};
match env_result.into() {
ReturnCode::Success => Ok(env),
_ => Err(get_error(
env as *mut c_void,
HandleType::Environment,
"Environment handle creation",
)),
}
}
fn create_server_handle(env: *const OCIEnv) -> Result<*mut OCIServer, OciError> {
match allocate_handle(env, HandleType::Server) {
Ok(server) => Ok(server as *mut OCIServer),
Err(err) => Err(err),
}
}
fn create_error_handle(env: *const OCIEnv) -> Result<*mut OCIError, OciError> {
match allocate_handle(env, HandleType::Error) {
Ok(error) => Ok(error as *mut OCIError),
Err(err) => Err(err),
}
}
fn create_service_handle(env: *const OCIEnv) -> Result<*mut OCISvcCtx, OciError> {
match allocate_handle(env, HandleType::Service) {
Ok(service) => Ok(service as *mut OCISvcCtx),
Err(err) => Err(err),
}
}
fn set_server_in_service(
service: *mut OCISvcCtx,
server: *mut OCIServer,
error: *mut OCIError,
) -> Result<(), OciError> {
let size: c_uint = 0;
set_handle_attribute(
service as *mut c_void,
HandleType::Service,
server as *mut c_void,
size,
AttributeType::Server,
error,
"Setting server in service handle",
)?;
Ok(())
}
fn create_session_handle(env: *const OCIEnv) -> Result<*mut OCISession, OciError> {
match allocate_handle(env, HandleType::Session) {
Ok(session) => Ok(session as *mut OCISession),
Err(err) => Err(err),
}
}
fn set_user_name_in_session(
session: *mut OCISession,
user_name: &str,
error: *mut OCIError,
) -> Result<(), OciError> {
let user_name_ptr = user_name.as_ptr();
let user_name_len = user_name.len() as c_uint;
set_handle_attribute(
session as *mut c_void,
HandleType::Session,
user_name_ptr as *mut c_void,
user_name_len,
AttributeType::UserName.into(),
error,
"Setting user name",
)?;
Ok(())
}
fn set_password_in_session(
session: *mut OCISession,
password: &str,
error: *mut OCIError,
) -> Result<(), OciError> {
let password_ptr = password.as_ptr();
let password_len = password.len() as c_uint;
set_handle_attribute(
session as *mut c_void,
HandleType::Session,
password_ptr as *mut c_void,
password_len,
AttributeType::Password.into(),
error,
"Setting password",
)?;
Ok(())
}
fn set_session_in_service(
service: *mut OCISvcCtx,
session: *mut OCISession,
error: *mut OCIError,
) -> Result<(), OciError> {
let size: c_uint = 0;
set_handle_attribute(
service as *mut c_void,
HandleType::Service.into(),
session as *mut c_void,
size,
AttributeType::Session,
error,
"Setting user session in service",
)?;
Ok(())
}
fn allocate_handle(env: *const OCIEnv, handle_type: HandleType) -> Result<*mut c_void, OciError> {
let handle: *mut c_void = ptr::null_mut();
let xtramem_sz: size_t = 0;
let null_ptr = ptr::null();
let allocation_result = unsafe {
OCIHandleAlloc(
env as *const c_void,
&handle,
handle_type.into(),
xtramem_sz,
null_ptr,
)
};
match allocation_result.into() {
ReturnCode::Success => Ok(handle),
_ => Err(get_error(
env as *mut c_void,
HandleType::Environment,
handle_type.into(),
)),
}
}
fn connect_to_database(
server: *mut OCIServer,
connection_str: &str,
error: *mut OCIError,
) -> Result<(), OciError> {
let conn_ptr = connection_str.as_ptr();
let conn_len = connection_str.len() as c_int;
let connect_result = unsafe {
OCIServerAttach(
server,
error,
conn_ptr,
conn_len,
EnvironmentMode::Default.into(),
)
};
match connect_result.into() {
ReturnCode::Success => Ok(()),
_ => Err(get_error(
error as *mut c_void,
HandleType::Error,
"Database connection",
)),
}
}
fn start_session(
service: *mut OCISvcCtx,
session: *mut OCISession,
error: *mut OCIError,
) -> Result<(), OciError> {
let session_result = unsafe {
OCISessionBegin(
service,
error,
session,
CredentialsType::Rdbms.into(),
EnvironmentMode::Default.into(),
)
};
match session_result.into() {
ReturnCode::Success => Ok(()),
_ => Err(get_error(
error as *mut c_void,
HandleType::Error,
"Starting user session",
)),
}
}