blob: b69ab587469bf1f18b388898fbde58cb5d6b7bb0 [file] [log] [blame]
extern crate libsqlite3_sys as ffi;
use std::ffi::{CStr, CString};
use std::io::{stderr, Write};
use std::os::raw as libc;
use std::{mem, ptr, slice, str};
use super::serialized_value::SerializedValue;
use result::Error::DatabaseError;
use result::*;
use util::NonNull;
#[allow(missing_debug_implementations, missing_copy_implementations)]
pub struct RawConnection {
pub(crate) internal_connection: NonNull<ffi::sqlite3>,
}
impl RawConnection {
pub fn establish(database_url: &str) -> ConnectionResult<Self> {
let mut conn_pointer = ptr::null_mut();
let database_url = try!(CString::new(database_url));
let connection_status =
unsafe { ffi::sqlite3_open(database_url.as_ptr(), &mut conn_pointer) };
match connection_status {
ffi::SQLITE_OK => {
let conn_pointer = unsafe { NonNull::new_unchecked(conn_pointer) };
Ok(RawConnection {
internal_connection: conn_pointer,
})
}
err_code => {
let message = super::error_message(err_code);
Err(ConnectionError::BadConnection(message.into()))
}
}
}
pub fn exec(&self, query: &str) -> QueryResult<()> {
let mut err_msg = ptr::null_mut();
let query = try!(CString::new(query));
let callback_fn = None;
let callback_arg = ptr::null_mut();
unsafe {
ffi::sqlite3_exec(
self.internal_connection.as_ptr(),
query.as_ptr(),
callback_fn,
callback_arg,
&mut err_msg,
);
}
if err_msg.is_null() {
Ok(())
} else {
let msg = convert_to_string_and_free(err_msg);
let error_kind = DatabaseErrorKind::__Unknown;
Err(DatabaseError(error_kind, Box::new(msg)))
}
}
pub fn rows_affected_by_last_query(&self) -> usize {
unsafe { ffi::sqlite3_changes(self.internal_connection.as_ptr()) as usize }
}
pub fn register_sql_function<F>(
&self,
fn_name: &str,
num_args: usize,
deterministic: bool,
f: F,
) -> QueryResult<()>
where
F: FnMut(&Self, &[*mut ffi::sqlite3_value]) -> QueryResult<SerializedValue>
+ Send
+ 'static,
{
let fn_name = CString::new(fn_name)?;
let mut flags = ffi::SQLITE_UTF8;
if deterministic {
flags |= ffi::SQLITE_DETERMINISTIC;
}
let callback_fn = Box::into_raw(Box::new(f));
let result = unsafe {
ffi::sqlite3_create_function_v2(
self.internal_connection.as_ptr(),
fn_name.as_ptr(),
num_args as _,
flags,
callback_fn as *mut _,
Some(run_custom_function::<F>),
None,
None,
Some(destroy_boxed_fn::<F>),
)
};
if result == ffi::SQLITE_OK {
Ok(())
} else {
let error_message = super::error_message(result);
Err(DatabaseError(
DatabaseErrorKind::__Unknown,
Box::new(error_message.to_string()),
))
}
}
}
impl Drop for RawConnection {
fn drop(&mut self) {
use std::thread::panicking;
let close_result = unsafe { ffi::sqlite3_close(self.internal_connection.as_ptr()) };
if close_result != ffi::SQLITE_OK {
let error_message = super::error_message(close_result);
if panicking() {
write!(
stderr(),
"Error closing SQLite connection: {}",
error_message
).expect("Error writing to `stderr`");
} else {
panic!("Error closing SQLite connection: {}", error_message);
}
}
}
}
fn convert_to_string_and_free(err_msg: *const libc::c_char) -> String {
let msg = unsafe {
let bytes = CStr::from_ptr(err_msg).to_bytes();
str::from_utf8_unchecked(bytes).into()
};
unsafe { ffi::sqlite3_free(err_msg as *mut libc::c_void) };
msg
}
#[allow(warnings)]
extern "C" fn run_custom_function<F>(
ctx: *mut ffi::sqlite3_context,
num_args: libc::c_int,
value_ptr: *mut *mut ffi::sqlite3_value,
) where
F: FnMut(&RawConnection, &[*mut ffi::sqlite3_value]) -> QueryResult<SerializedValue>
+ Send
+ 'static,
{
static NULL_DATA_ERR: &str = "An unknown error occurred. sqlite3_user_data returned a null pointer. This should never happen.";
static NULL_CONN_ERR: &str = "An unknown error occurred. sqlite3_context_db_handle returned a null pointer. This should never happen.";
unsafe {
let data_ptr = ffi::sqlite3_user_data(ctx);
let data_ptr = data_ptr as *mut F;
let f = match data_ptr.as_mut() {
Some(f) => f,
None => {
ffi::sqlite3_result_error(
ctx,
NULL_DATA_ERR.as_ptr() as *const _ as *const _,
NULL_DATA_ERR.len() as _,
);
return;
}
};
let args = slice::from_raw_parts(value_ptr, num_args as _);
let conn = match NonNull::new(ffi::sqlite3_context_db_handle(ctx)) {
Some(conn) => RawConnection {
internal_connection: conn,
},
None => {
ffi::sqlite3_result_error(
ctx,
NULL_DATA_ERR.as_ptr() as *const _ as *const _,
NULL_DATA_ERR.len() as _,
);
return;
}
};
match f(&conn, args) {
Ok(value) => value.result_of(ctx),
Err(e) => {
let msg = e.to_string();
ffi::sqlite3_result_error(ctx, msg.as_ptr() as *const _, msg.len() as _);
}
}
mem::forget(conn);
}
}
extern "C" fn destroy_boxed_fn<F>(data: *mut libc::c_void)
where
F: FnMut(&RawConnection, &[*mut ffi::sqlite3_value]) -> QueryResult<SerializedValue>
+ Send
+ 'static,
{
let ptr = data as *mut F;
unsafe { Box::from_raw(ptr) };
}