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