diesel/mysql/connection/
raw.rs
1#![allow(unsafe_code)] use mysqlclient_sys as ffi;
3use std::ffi::CStr;
4use std::os::raw as libc;
5use std::ptr::{self, NonNull};
6use std::sync::Once;
7
8use super::statement_cache::PrepareForCache;
9use super::stmt::Statement;
10use super::url::ConnectionOptions;
11use crate::mysql::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 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
66 if let Some(ssl_mode) = connection_options.ssl_mode() {
67 self.set_ssl_mode(ssl_mode)
68 }
69 if let Some(ssl_ca) = connection_options.ssl_ca() {
70 self.set_ssl_ca(ssl_ca)
71 }
72 if let Some(ssl_cert) = connection_options.ssl_cert() {
73 self.set_ssl_cert(ssl_cert)
74 }
75 if let Some(ssl_key) = connection_options.ssl_key() {
76 self.set_ssl_key(ssl_key)
77 }
78
79 unsafe {
80 ffi::mysql_real_connect(
82 self.0.as_ptr(),
83 host.map(CStr::as_ptr).unwrap_or_else(ptr::null),
84 user.as_ptr(),
85 password.map(CStr::as_ptr).unwrap_or_else(ptr::null),
86 database.map(CStr::as_ptr).unwrap_or_else(ptr::null),
87 u32::from(port.unwrap_or(0)),
88 unix_socket.map(CStr::as_ptr).unwrap_or_else(ptr::null),
89 client_flags.bits().into(),
90 )
91 };
92
93 let last_error_message = self.last_error_message();
94 if last_error_message.is_empty() {
95 Ok(())
96 } else {
97 Err(ConnectionError::BadConnection(last_error_message))
98 }
99 }
100
101 pub(super) fn last_error_message(&self) -> String {
102 unsafe { CStr::from_ptr(ffi::mysql_error(self.0.as_ptr())) }
103 .to_string_lossy()
104 .into_owned()
105 }
106
107 pub(super) fn execute(&self, query: &str) -> QueryResult<()> {
108 unsafe {
109 ffi::mysql_real_query(
111 self.0.as_ptr(),
112 query.as_ptr() as *const libc::c_char,
113 query.len() as libc::c_ulong,
114 );
115 }
116 self.did_an_error_occur()?;
117 self.flush_pending_results()?;
118 Ok(())
119 }
120
121 pub(super) fn enable_multi_statements<T, F>(&self, f: F) -> QueryResult<T>
122 where
123 F: FnOnce() -> QueryResult<T>,
124 {
125 unsafe {
126 ffi::mysql_set_server_option(
127 self.0.as_ptr(),
128 ffi::enum_mysql_set_option::MYSQL_OPTION_MULTI_STATEMENTS_ON,
129 );
130 }
131 self.did_an_error_occur()?;
132
133 let result = f();
134
135 unsafe {
136 ffi::mysql_set_server_option(
137 self.0.as_ptr(),
138 ffi::enum_mysql_set_option::MYSQL_OPTION_MULTI_STATEMENTS_OFF,
139 );
140 }
141 self.did_an_error_occur()?;
142
143 result
144 }
145
146 pub(super) fn prepare(
147 &self,
148 query: &str,
149 _: PrepareForCache,
150 _: &[MysqlType],
151 ) -> QueryResult<Statement> {
152 let stmt = unsafe { ffi::mysql_stmt_init(self.0.as_ptr()) };
153 let stmt = NonNull::new(stmt).expect("Out of memory creating prepared statement");
157 let stmt = Statement::new(stmt);
158 stmt.prepare(query)?;
159 Ok(stmt)
160 }
161
162 fn did_an_error_occur(&self) -> QueryResult<()> {
163 use crate::result::DatabaseErrorKind;
164 use crate::result::Error::DatabaseError;
165
166 let error_message = self.last_error_message();
167 if error_message.is_empty() {
168 Ok(())
169 } else {
170 Err(DatabaseError(
171 DatabaseErrorKind::Unknown,
172 Box::new(error_message),
173 ))
174 }
175 }
176
177 fn flush_pending_results(&self) -> QueryResult<()> {
178 self.consume_current_result()?;
180 while self.more_results() {
181 self.next_result()?;
182 self.consume_current_result()?;
183 }
184 Ok(())
185 }
186
187 fn consume_current_result(&self) -> QueryResult<()> {
188 unsafe {
189 let res = ffi::mysql_store_result(self.0.as_ptr());
190 if !res.is_null() {
191 ffi::mysql_free_result(res);
192 }
193 }
194 self.did_an_error_occur()
195 }
196
197 fn more_results(&self) -> bool {
198 unsafe { ffi::mysql_more_results(self.0.as_ptr()) != ffi_false() }
199 }
200
201 fn next_result(&self) -> QueryResult<()> {
202 unsafe { ffi::mysql_next_result(self.0.as_ptr()) };
203 self.did_an_error_occur()
204 }
205
206 fn set_ssl_mode(&self, ssl_mode: mysqlclient_sys::mysql_ssl_mode) {
207 let v = ssl_mode as u32;
208 let v_ptr: *const u32 = &v;
209 let n = ptr::NonNull::new(v_ptr as *mut u32).expect("NonNull::new failed");
210 unsafe {
211 mysqlclient_sys::mysql_options(
212 self.0.as_ptr(),
213 mysqlclient_sys::mysql_option::MYSQL_OPT_SSL_MODE,
214 n.as_ptr() as *const std::ffi::c_void,
215 )
216 };
217 }
218
219 fn set_ssl_ca(&self, ssl_ca: &CStr) {
220 unsafe {
221 mysqlclient_sys::mysql_options(
222 self.0.as_ptr(),
223 mysqlclient_sys::mysql_option::MYSQL_OPT_SSL_CA,
224 ssl_ca.as_ptr() as *const std::ffi::c_void,
225 )
226 };
227 }
228
229 fn set_ssl_cert(&self, ssl_cert: &CStr) {
230 unsafe {
231 mysqlclient_sys::mysql_options(
232 self.0.as_ptr(),
233 mysqlclient_sys::mysql_option::MYSQL_OPT_SSL_CERT,
234 ssl_cert.as_ptr() as *const std::ffi::c_void,
235 )
236 };
237 }
238
239 fn set_ssl_key(&self, ssl_key: &CStr) {
240 unsafe {
241 mysqlclient_sys::mysql_options(
242 self.0.as_ptr(),
243 mysqlclient_sys::mysql_option::MYSQL_OPT_SSL_KEY,
244 ssl_key.as_ptr() as *const std::ffi::c_void,
245 )
246 };
247 }
248}
249
250impl Drop for RawConnection {
251 fn drop(&mut self) {
252 unsafe {
253 ffi::mysql_close(self.0.as_ptr());
254 }
255 }
256}
257
258static MYSQL_THREAD_UNSAFE_INIT: Once = Once::new();
268
269fn perform_thread_unsafe_library_initialization() {
270 MYSQL_THREAD_UNSAFE_INIT.call_once(|| {
271 let error_code = unsafe { ffi::mysql_server_init(0, ptr::null_mut(), ptr::null_mut()) };
274 if error_code != 0 {
275 panic!("Unable to perform MySQL global initialization");
282 }
283 })
284}