diesel/mysql_like/connection/
raw.rs1#![allow(unsafe_code)] use core::ffi as libc;
3use core::ffi::CStr;
4use core::ptr::{self, NonNull};
5use mysqlclient_sys as ffi;
6use std::sync::Once;
7
8use super::error_information::{MysqlLikeErrorInformation, sqlstate_from_ptr};
9use super::statement_cache::PrepareForCache;
10use super::stmt::Statement;
11use super::url::ConnectionOptions;
12use crate::mysql_like::{MysqlLikeBackend, MysqlType};
13use crate::result::{ConnectionError, ConnectionResult, QueryResult, SqlState};
14
15pub(super) struct RawConnection(NonNull<ffi::MYSQL>);
16
17#[inline(always)]
26pub(super) fn ffi_false() -> ffi::my_bool {
27 Default::default()
28}
29
30impl RawConnection {
31 pub(super) fn new() -> Self {
32 perform_thread_unsafe_library_initialization();
33 let raw_connection = unsafe { ffi::mysql_init(ptr::null_mut()) };
34 let raw_connection =
37 NonNull::new(raw_connection).expect("Insufficient memory to allocate connection");
38 let result = RawConnection(raw_connection);
39
40 let charset_result = unsafe {
42 ffi::mysql_options(
43 result.0.as_ptr(),
44 ffi::mysql_option::MYSQL_SET_CHARSET_NAME,
45 c"utf8mb4".as_ptr() as *const libc::c_void,
46 )
47 };
48 {
match (&0, &charset_result) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val,
::core::option::Option::Some(format_args!("MYSQL_SET_CHARSET_NAME was not recognized as an option by MySQL. This should never happen.")));
}
}
}
};assert_eq!(
49 0, charset_result,
50 "MYSQL_SET_CHARSET_NAME was not \
51 recognized as an option by MySQL. This should never \
52 happen."
53 );
54
55 result
56 }
57
58 pub(super) fn connect(&self, connection_options: &ConnectionOptions) -> ConnectionResult<()> {
59 let host = connection_options.host();
60 let user = connection_options.user();
61 let password = connection_options.password();
62 let database = connection_options.database();
63 let port = connection_options.port();
64 let unix_socket = connection_options.unix_socket();
65 let client_flags = connection_options.client_flags();
66 let local_infile = connection_options.local_infile();
67
68 if let Some(ssl_mode) = connection_options.ssl_mode() {
69 self.set_ssl_mode(ssl_mode)
70 }
71 if let Some(ssl_ca) = connection_options.ssl_ca() {
72 self.set_ssl_ca(ssl_ca)
73 }
74 if let Some(ssl_cert) = connection_options.ssl_cert() {
75 self.set_ssl_cert(ssl_cert)
76 }
77 if let Some(ssl_key) = connection_options.ssl_key() {
78 self.set_ssl_key(ssl_key)
79 }
80 if let Some(local_infile) = local_infile {
81 self.set_local_infile(local_infile)
82 }
83
84 unsafe {
85 ffi::mysql_real_connect(
87 self.0.as_ptr(),
88 host.map(CStr::as_ptr).unwrap_or_else(ptr::null),
89 user.as_ptr(),
90 password.map(CStr::as_ptr).unwrap_or_else(ptr::null),
91 database.map(CStr::as_ptr).unwrap_or_else(ptr::null),
92 u32::from(port.unwrap_or(0)),
93 unix_socket.map(CStr::as_ptr).unwrap_or_else(ptr::null),
94 client_flags.bits().into(),
95 )
96 };
97
98 let last_error_message = self.last_error_message();
99 if last_error_message.is_empty() {
100 let v = true as u32;
101 let v_ptr: *const u32 = &v;
102 unsafe {
104 ffi::mysql_options(
105 self.0.as_ptr(),
106 ffi::mysql_option::MYSQL_REPORT_DATA_TRUNCATION,
107 v_ptr as *const libc::c_void,
108 );
109 }
110 Ok(())
111 } else {
112 Err(ConnectionError::BadConnection(last_error_message))
113 }
114 }
115
116 pub(super) fn last_error_message(&self) -> String {
117 unsafe { CStr::from_ptr(ffi::mysql_error(self.0.as_ptr())) }
118 .to_string_lossy()
119 .into_owned()
120 }
121
122 fn last_error_sqlstate(&self) -> Option<SqlState> {
123 let sqlstate = unsafe { ffi::mysql_sqlstate(self.0.as_ptr()) };
125 unsafe { sqlstate_from_ptr(sqlstate) }
128 }
129
130 pub(super) fn server_info(&self) -> Option<&CStr> {
131 let info = unsafe { ffi::mysql_get_server_info(self.0.as_ptr()) };
133 (!info.is_null()).then(|| unsafe { CStr::from_ptr(info) })
137 }
138
139 pub(super) fn execute(&self, query: &str) -> QueryResult<()> {
140 unsafe {
141 ffi::mysql_real_query(
143 self.0.as_ptr(),
144 query.as_ptr() as *const libc::c_char,
145 query.len() as libc::c_ulong,
146 );
147 }
148 self.did_an_error_occur()?;
149 self.flush_pending_results()?;
150 Ok(())
151 }
152
153 pub(super) fn enable_multi_statements<T, F>(&self, f: F) -> QueryResult<T>
154 where
155 F: FnOnce() -> QueryResult<T>,
156 {
157 unsafe {
158 ffi::mysql_set_server_option(
159 self.0.as_ptr(),
160 ffi::enum_mysql_set_option::MYSQL_OPTION_MULTI_STATEMENTS_ON,
161 );
162 }
163 self.did_an_error_occur()?;
164
165 let result = f();
166
167 unsafe {
168 ffi::mysql_set_server_option(
169 self.0.as_ptr(),
170 ffi::enum_mysql_set_option::MYSQL_OPTION_MULTI_STATEMENTS_OFF,
171 );
172 }
173 self.did_an_error_occur()?;
174
175 result
176 }
177
178 pub(super) fn prepare<DB: MysqlLikeBackend>(
179 &self,
180 query: &str,
181 _: PrepareForCache,
182 _: &[MysqlType],
183 ) -> QueryResult<Statement<DB>> {
184 let stmt = unsafe { ffi::mysql_stmt_init(self.0.as_ptr()) };
185 let stmt = NonNull::new(stmt).expect("Out of memory creating prepared statement");
189 let stmt = Statement::new(stmt);
190 stmt.prepare(query)?;
191 Ok(stmt)
192 }
193
194 fn did_an_error_occur(&self) -> QueryResult<()> {
195 use crate::result::DatabaseErrorKind;
196 use crate::result::Error::DatabaseError;
197
198 let error_message = self.last_error_message();
199 if error_message.is_empty() {
200 Ok(())
201 } else {
202 Err(DatabaseError(
203 DatabaseErrorKind::Unknown,
204 Box::new(MysqlLikeErrorInformation {
205 message: error_message,
206 sqlstate: self.last_error_sqlstate(),
207 }),
208 ))
209 }
210 }
211
212 fn flush_pending_results(&self) -> QueryResult<()> {
213 self.consume_current_result()?;
215 while self.more_results() {
216 self.next_result()?;
217 self.consume_current_result()?;
218 }
219 Ok(())
220 }
221
222 fn consume_current_result(&self) -> QueryResult<()> {
223 unsafe {
224 let res = ffi::mysql_store_result(self.0.as_ptr());
225 if !res.is_null() {
226 ffi::mysql_free_result(res);
227 }
228 }
229 self.did_an_error_occur()
230 }
231
232 fn more_results(&self) -> bool {
233 unsafe { ffi::mysql_more_results(self.0.as_ptr()) != ffi_false() }
234 }
235
236 fn next_result(&self) -> QueryResult<()> {
237 unsafe { ffi::mysql_next_result(self.0.as_ptr()) };
238 self.did_an_error_occur()
239 }
240
241 fn set_ssl_mode(&self, ssl_mode: mysqlclient_sys::mysql_ssl_mode) {
242 let v = ssl_mode as u32;
243 let v_ptr: *const u32 = &v;
244 let n = ptr::NonNull::new(v_ptr as *mut u32).expect("NonNull::new failed");
245 unsafe {
246 mysqlclient_sys::mysql_options(
247 self.0.as_ptr(),
248 mysqlclient_sys::mysql_option::MYSQL_OPT_SSL_MODE,
249 n.as_ptr() as *const core::ffi::c_void,
250 )
251 };
252 }
253
254 fn set_ssl_ca(&self, ssl_ca: &CStr) {
255 unsafe {
256 mysqlclient_sys::mysql_options(
257 self.0.as_ptr(),
258 mysqlclient_sys::mysql_option::MYSQL_OPT_SSL_CA,
259 ssl_ca.as_ptr() as *const core::ffi::c_void,
260 )
261 };
262 }
263
264 fn set_ssl_cert(&self, ssl_cert: &CStr) {
265 unsafe {
266 mysqlclient_sys::mysql_options(
267 self.0.as_ptr(),
268 mysqlclient_sys::mysql_option::MYSQL_OPT_SSL_CERT,
269 ssl_cert.as_ptr() as *const core::ffi::c_void,
270 )
271 };
272 }
273
274 fn set_ssl_key(&self, ssl_key: &CStr) {
275 unsafe {
276 mysqlclient_sys::mysql_options(
277 self.0.as_ptr(),
278 mysqlclient_sys::mysql_option::MYSQL_OPT_SSL_KEY,
279 ssl_key.as_ptr() as *const core::ffi::c_void,
280 )
281 };
282 }
283
284 fn set_local_infile(&self, local_infile: bool) {
285 let v = local_infile as u32;
286 let v_ptr: *const u32 = &v;
287 let n = ptr::NonNull::new(v_ptr as *mut u32).expect("NonNull::new failed");
288 unsafe {
289 mysqlclient_sys::mysql_options(
290 self.0.as_ptr(),
291 mysqlclient_sys::mysql_option::MYSQL_OPT_LOCAL_INFILE,
292 n.as_ptr() as *const core::ffi::c_void,
293 )
294 };
295 }
296}
297
298impl Drop for RawConnection {
299 fn drop(&mut self) {
300 unsafe {
301 ffi::mysql_close(self.0.as_ptr());
302 }
303 }
304}
305
306static MYSQL_THREAD_UNSAFE_INIT: Once = Once::new();
316
317fn perform_thread_unsafe_library_initialization() {
318 MYSQL_THREAD_UNSAFE_INIT.call_once(|| {
319 let error_code = unsafe { ffi::mysql_server_init(0, ptr::null_mut(), ptr::null_mut()) };
322 if error_code != 0 {
323 {
::core::panicking::panic_fmt(format_args!("Unable to perform MySQL global initialization"));
};panic!("Unable to perform MySQL global initialization");
330 }
331 })
332}