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