1 extern crate libsqlite3_sys as ffi;
2 
3 use std::ffi::{CStr, CString};
4 use std::io::{stderr, Write};
5 use std::os::raw as libc;
6 use std::ptr::NonNull;
7 use std::{mem, ptr, slice, str};
8 
9 use super::serialized_value::SerializedValue;
10 use result::Error::DatabaseError;
11 use result::*;
12 
13 #[allow(missing_debug_implementations, missing_copy_implementations)]
14 pub struct RawConnection {
15     pub(crate) internal_connection: NonNull<ffi::sqlite3>,
16 }
17 
18 impl RawConnection {
establish(database_url: &str) -> ConnectionResult<Self>19     pub fn establish(database_url: &str) -> ConnectionResult<Self> {
20         let mut conn_pointer = ptr::null_mut();
21         let database_url = CString::new(database_url)?;
22         let connection_status =
23             unsafe { ffi::sqlite3_open(database_url.as_ptr(), &mut conn_pointer) };
24 
25         match connection_status {
26             ffi::SQLITE_OK => {
27                 let conn_pointer = unsafe { NonNull::new_unchecked(conn_pointer) };
28                 Ok(RawConnection {
29                     internal_connection: conn_pointer,
30                 })
31             }
32             err_code => {
33                 let message = super::error_message(err_code);
34                 Err(ConnectionError::BadConnection(message.into()))
35             }
36         }
37     }
38 
exec(&self, query: &str) -> QueryResult<()>39     pub fn exec(&self, query: &str) -> QueryResult<()> {
40         let mut err_msg = ptr::null_mut();
41         let query = CString::new(query)?;
42         let callback_fn = None;
43         let callback_arg = ptr::null_mut();
44         unsafe {
45             ffi::sqlite3_exec(
46                 self.internal_connection.as_ptr(),
47                 query.as_ptr(),
48                 callback_fn,
49                 callback_arg,
50                 &mut err_msg,
51             );
52         }
53 
54         if err_msg.is_null() {
55             Ok(())
56         } else {
57             let msg = convert_to_string_and_free(err_msg);
58             let error_kind = DatabaseErrorKind::__Unknown;
59             Err(DatabaseError(error_kind, Box::new(msg)))
60         }
61     }
62 
rows_affected_by_last_query(&self) -> usize63     pub fn rows_affected_by_last_query(&self) -> usize {
64         unsafe { ffi::sqlite3_changes(self.internal_connection.as_ptr()) as usize }
65     }
66 
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,67     pub fn register_sql_function<F>(
68         &self,
69         fn_name: &str,
70         num_args: usize,
71         deterministic: bool,
72         f: F,
73     ) -> QueryResult<()>
74     where
75         F: FnMut(&Self, &[*mut ffi::sqlite3_value]) -> QueryResult<SerializedValue>
76             + Send
77             + 'static,
78     {
79         let fn_name = CString::new(fn_name)?;
80         let mut flags = ffi::SQLITE_UTF8;
81         if deterministic {
82             flags |= ffi::SQLITE_DETERMINISTIC;
83         }
84         let callback_fn = Box::into_raw(Box::new(f));
85 
86         let result = unsafe {
87             ffi::sqlite3_create_function_v2(
88                 self.internal_connection.as_ptr(),
89                 fn_name.as_ptr(),
90                 num_args as _,
91                 flags,
92                 callback_fn as *mut _,
93                 Some(run_custom_function::<F>),
94                 None,
95                 None,
96                 Some(destroy_boxed_fn::<F>),
97             )
98         };
99 
100         if result == ffi::SQLITE_OK {
101             Ok(())
102         } else {
103             let error_message = super::error_message(result);
104             Err(DatabaseError(
105                 DatabaseErrorKind::__Unknown,
106                 Box::new(error_message.to_string()),
107             ))
108         }
109     }
110 }
111 
112 impl Drop for RawConnection {
drop(&mut self)113     fn drop(&mut self) {
114         use std::thread::panicking;
115 
116         let close_result = unsafe { ffi::sqlite3_close(self.internal_connection.as_ptr()) };
117         if close_result != ffi::SQLITE_OK {
118             let error_message = super::error_message(close_result);
119             if panicking() {
120                 write!(
121                     stderr(),
122                     "Error closing SQLite connection: {}",
123                     error_message
124                 )
125                 .expect("Error writing to `stderr`");
126             } else {
127                 panic!("Error closing SQLite connection: {}", error_message);
128             }
129         }
130     }
131 }
132 
convert_to_string_and_free(err_msg: *const libc::c_char) -> String133 fn convert_to_string_and_free(err_msg: *const libc::c_char) -> String {
134     let msg = unsafe {
135         let bytes = CStr::from_ptr(err_msg).to_bytes();
136         // sqlite is documented to return utf8 strings here
137         str::from_utf8_unchecked(bytes).into()
138     };
139     unsafe { ffi::sqlite3_free(err_msg as *mut libc::c_void) };
140     msg
141 }
142 
143 #[allow(warnings)]
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,144 extern "C" fn run_custom_function<F>(
145     ctx: *mut ffi::sqlite3_context,
146     num_args: libc::c_int,
147     value_ptr: *mut *mut ffi::sqlite3_value,
148 ) where
149     F: FnMut(&RawConnection, &[*mut ffi::sqlite3_value]) -> QueryResult<SerializedValue>
150         + Send
151         + 'static,
152 {
153     static NULL_DATA_ERR: &str = "An unknown error occurred. sqlite3_user_data returned a null pointer. This should never happen.";
154     static NULL_CONN_ERR: &str = "An unknown error occurred. sqlite3_context_db_handle returned a null pointer. This should never happen.";
155 
156     unsafe {
157         let data_ptr = ffi::sqlite3_user_data(ctx);
158         let data_ptr = data_ptr as *mut F;
159         let f = match data_ptr.as_mut() {
160             Some(f) => f,
161             None => {
162                 ffi::sqlite3_result_error(
163                     ctx,
164                     NULL_DATA_ERR.as_ptr() as *const _ as *const _,
165                     NULL_DATA_ERR.len() as _,
166                 );
167                 return;
168             }
169         };
170 
171         let args = slice::from_raw_parts(value_ptr, num_args as _);
172         let conn = match NonNull::new(ffi::sqlite3_context_db_handle(ctx)) {
173             Some(conn) => RawConnection {
174                 internal_connection: conn,
175             },
176             None => {
177                 ffi::sqlite3_result_error(
178                     ctx,
179                     NULL_DATA_ERR.as_ptr() as *const _ as *const _,
180                     NULL_DATA_ERR.len() as _,
181                 );
182                 return;
183             }
184         };
185         match f(&conn, args) {
186             Ok(value) => value.result_of(ctx),
187             Err(e) => {
188                 let msg = e.to_string();
189                 ffi::sqlite3_result_error(ctx, msg.as_ptr() as *const _, msg.len() as _);
190             }
191         }
192 
193         mem::forget(conn);
194     }
195 }
196 
destroy_boxed_fn<F>(data: *mut libc::c_void) where F: FnMut(&RawConnection, &[*mut ffi::sqlite3_value]) -> QueryResult<SerializedValue> + Send + 'static,197 extern "C" fn destroy_boxed_fn<F>(data: *mut libc::c_void)
198 where
199     F: FnMut(&RawConnection, &[*mut ffi::sqlite3_value]) -> QueryResult<SerializedValue>
200         + Send
201         + 'static,
202 {
203     let ptr = data as *mut F;
204     unsafe { Box::from_raw(ptr) };
205 }
206