diesel/mysql_like/connection/
mod.rs1mod bind;
2mod error_information;
3mod raw;
4mod stmt;
5mod url;
6
7use core::num::NonZeroU64;
8
9use self::raw::RawConnection;
10use self::stmt::Statement;
11use self::stmt::iterator::StatementIterator;
12use self::url::ConnectionOptions;
13use crate::RunQueryDsl;
14use crate::connection::instrumentation::{DebugQuery, DynInstrumentation, StrQueryHelper};
15use crate::connection::statement_cache::{MaybeCached, StatementCache};
16use crate::connection::*;
17use crate::expression::QueryMetadata;
18use crate::mysql_like::MysqlLikeBackend;
19use crate::query_builder::bind_collector::RawBytesBindCollector;
20use crate::query_builder::*;
21use crate::result::*;
22
23#[allow(missing_debug_implementations, missing_copy_implementations)]
24pub struct MysqlLikeConnection<DB: MysqlLikeBackend> {
113 raw_connection: RawConnection,
114 transaction_state: AnsiTransactionManager,
115 statement_cache: StatementCache<DB, Statement<DB>>,
116 instrumentation: DynInstrumentation,
117}
118
119#[allow(unsafe_code)]
121unsafe impl<DB: MysqlLikeBackend> Send for MysqlLikeConnection<DB> {}
122
123impl<DB: MysqlLikeBackend> SimpleConnection for MysqlLikeConnection<DB> {
124 fn batch_execute(&mut self, query: &str) -> QueryResult<()> {
125 self.instrumentation
126 .on_connection_event(InstrumentationEvent::StartQuery {
127 query: &StrQueryHelper::new(query),
128 });
129 let r = self
130 .raw_connection
131 .enable_multi_statements(|| self.raw_connection.execute(query));
132 self.instrumentation
133 .on_connection_event(InstrumentationEvent::FinishQuery {
134 query: &StrQueryHelper::new(query),
135 error: r.as_ref().err(),
136 });
137 r
138 }
139}
140
141impl<DB: MysqlLikeBackend> ConnectionSealed for MysqlLikeConnection<DB> {}
142
143impl<DB: MysqlLikeBackend> Connection for MysqlLikeConnection<DB> {
144 type Backend = DB;
145 type TransactionManager = AnsiTransactionManager;
146
147 fn establish(database_url: &str) -> ConnectionResult<Self> {
162 let mut instrumentation = DynInstrumentation::default_instrumentation();
163 instrumentation.on_connection_event(InstrumentationEvent::StartEstablishConnection {
164 url: database_url,
165 });
166
167 let establish_result = Self::establish_inner(database_url);
168 instrumentation.on_connection_event(InstrumentationEvent::FinishEstablishConnection {
169 url: database_url,
170 error: establish_result.as_ref().err(),
171 });
172 let mut conn = establish_result?;
173 conn.instrumentation = instrumentation;
174 Ok(conn)
175 }
176
177 fn execute_returning_count<T>(&mut self, source: &T) -> QueryResult<usize>
178 where
179 T: QueryFragment<Self::Backend> + QueryId,
180 {
181 #[allow(unsafe_code)] update_transaction_manager_status(
183 prepared_query(
184 &source,
185 &mut self.statement_cache,
186 &mut self.raw_connection,
187 &mut *self.instrumentation,
188 )
189 .and_then(|stmt| {
190 let stmt_use = unsafe { stmt.execute() }?;
193 stmt_use.affected_rows()
194 }),
195 &mut self.transaction_state,
196 &mut self.instrumentation,
197 &crate::debug_query(source),
198 )
199 }
200
201 fn transaction_state(&mut self) -> &mut AnsiTransactionManager {
202 &mut self.transaction_state
203 }
204
205 fn instrumentation(&mut self) -> &mut dyn Instrumentation {
206 &mut *self.instrumentation
207 }
208
209 fn set_instrumentation(&mut self, instrumentation: impl Instrumentation) {
210 self.instrumentation = instrumentation.into();
211 }
212
213 fn set_prepared_statement_cache_size(&mut self, size: CacheSize) {
214 self.statement_cache.set_cache_size(size);
215 }
216}
217
218#[inline(always)]
219fn update_transaction_manager_status<T>(
220 query_result: QueryResult<T>,
221 transaction_manager: &mut AnsiTransactionManager,
222 instrumentation: &mut DynInstrumentation,
223 query: &dyn DebugQuery,
224) -> QueryResult<T> {
225 fn non_generic_inner(
226 query_result: Result<(), &Error>,
227 transaction_manager: &mut AnsiTransactionManager,
228 instrumentation: &mut DynInstrumentation,
229 query: &dyn DebugQuery,
230 ) {
231 if let Err(Error::DatabaseError(DatabaseErrorKind::SerializationFailure, _)) = query_result
232 {
233 transaction_manager
234 .status
235 .set_requires_rollback_maybe_up_to_top_level(true)
236 }
237 instrumentation.on_connection_event(InstrumentationEvent::FinishQuery {
238 query,
239 error: query_result.err(),
240 });
241 }
242
243 non_generic_inner(
244 query_result.as_ref().map(|_| ()),
245 transaction_manager,
246 instrumentation,
247 query,
248 );
249 query_result
250}
251
252impl<DB: MysqlLikeBackend> LoadConnection<DefaultLoadingMode> for MysqlLikeConnection<DB> {
253 type Cursor<'conn, 'query> = self::stmt::iterator::StatementIterator<'conn, DB>;
254 type Row<'conn, 'query> = self::stmt::iterator::MysqlRow<DB>;
255
256 fn load<'conn, 'query, T>(
257 &'conn mut self,
258 source: T,
259 ) -> QueryResult<Self::Cursor<'conn, 'query>>
260 where
261 T: Query + QueryFragment<Self::Backend> + QueryId + 'query,
262 Self::Backend: QueryMetadata<T::SqlType>,
263 {
264 update_transaction_manager_status(
265 prepared_query(
266 &source,
267 &mut self.statement_cache,
268 &mut self.raw_connection,
269 &mut *self.instrumentation,
270 )
271 .and_then(|stmt| {
272 let mut metadata = Vec::new();
273 DB::row_metadata(&mut (), &mut metadata);
274 StatementIterator::from_stmt(stmt, &metadata)
275 }),
276 &mut self.transaction_state,
277 &mut self.instrumentation,
278 &crate::debug_query(&source),
279 )
280 }
281}
282
283#[cfg(feature = "r2d2")]
284impl<DB: MysqlLikeBackend> crate::r2d2::R2D2Connection for MysqlLikeConnection<DB> {
285 fn ping(&mut self) -> QueryResult<()> {
286 crate::r2d2::CheckConnectionQuery.execute(self).map(|_| ())
287 }
288
289 fn is_broken(&mut self) -> bool {
290 AnsiTransactionManager::is_broken_transaction_manager(self)
291 }
292}
293
294impl<DB: MysqlLikeBackend> MultiConnectionHelper for MysqlLikeConnection<DB> {
295 fn to_any<'a>(
296 lookup: &mut <Self::Backend as crate::sql_types::TypeMetadata>::MetadataLookup,
297 ) -> &mut (dyn core::any::Any + 'a) {
298 lookup
299 }
300
301 fn from_any(
302 lookup: &mut dyn core::any::Any,
303 ) -> Option<&mut <Self::Backend as crate::sql_types::TypeMetadata>::MetadataLookup> {
304 lookup.downcast_mut()
305 }
306}
307
308fn prepared_query<'a, DB: MysqlLikeBackend + Default, T: QueryFragment<DB> + QueryId>(
309 source: &'_ T,
310 statement_cache: &'a mut StatementCache<DB, Statement<DB>>,
311 raw_connection: &'a mut RawConnection,
312 instrumentation: &mut dyn Instrumentation,
313) -> QueryResult<MaybeCached<'a, Statement<DB>>> {
314 instrumentation.on_connection_event(InstrumentationEvent::StartQuery {
315 query: &crate::debug_query(source),
316 });
317 let mut stmt = statement_cache.cached_statement(
318 source,
319 &DB::default(),
320 &[],
321 &*raw_connection,
322 RawConnection::prepare,
323 instrumentation,
324 )?;
325
326 let mut bind_collector = RawBytesBindCollector::new();
327 source.collect_binds(&mut bind_collector, &mut (), &DB::default())?;
328 let binds = bind_collector
329 .metadata
330 .into_iter()
331 .zip(bind_collector.binds);
332 stmt.bind(binds)?;
333 Ok(stmt)
334}
335
336impl<DB: MysqlLikeBackend> MysqlLikeConnection<DB> {
337 pub fn version(&self) -> QueryResult<DatabaseVersion> {
360 let info = self.raw_connection.server_info().ok_or_else(|| {
361 Error::DeserializationError("The server did not report its version".into())
362 })?;
363 parse_server_version(info.to_bytes()).ok_or_else(|| {
364 Error::DeserializationError(
365 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("Unrecognized server version `{0}`",
info.to_string_lossy()))
})format!("Unrecognized server version `{}`", info.to_string_lossy()).into(),
366 )
367 })
368 }
369
370 pub(crate) fn execute_returning_id<T>(&mut self, source: &T) -> QueryResult<Option<NonZeroU64>>
373 where
374 T: QueryFragment<DB> + QueryId,
375 {
376 #[allow(unsafe_code)] update_transaction_manager_status(
378 prepared_query(
379 &source,
380 &mut self.statement_cache,
381 &mut self.raw_connection,
382 &mut *self.instrumentation,
383 )
384 .and_then(|stmt| {
385 let stmt_use = unsafe { stmt.execute() }?;
389 Ok(NonZeroU64::new(stmt_use.insert_id()))
390 }),
391 &mut self.transaction_state,
392 &mut self.instrumentation,
393 &crate::debug_query(source),
394 )
395 }
396
397 fn set_config_options(&mut self) -> QueryResult<()> {
398 crate::sql_query("SET time_zone = '+00:00';").execute(self)?;
399 crate::sql_query("SET character_set_client = 'utf8mb4'").execute(self)?;
400 crate::sql_query("SET character_set_connection = 'utf8mb4'").execute(self)?;
401 crate::sql_query("SET character_set_results = 'utf8mb4'").execute(self)?;
402 Ok(())
403 }
404
405 fn establish_inner(database_url: &str) -> Result<MysqlLikeConnection<DB>, ConnectionError> {
406 use crate::ConnectionError::CouldntSetupConfiguration;
407
408 let raw_connection = RawConnection::new();
409 let connection_options = ConnectionOptions::parse::<DB>(database_url)?;
410 raw_connection.connect(&connection_options)?;
411 let mut conn = MysqlLikeConnection {
412 raw_connection,
413 transaction_state: AnsiTransactionManager::default(),
414 statement_cache: StatementCache::new(),
415 instrumentation: DynInstrumentation::none(),
416 };
417 conn.set_config_options()
418 .map_err(CouldntSetupConfiguration)?;
419 Ok(conn)
420 }
421}
422
423const MARIADB_REPLICATION_VERSION_PREFIX: &[u8] = b"5.5.5-";
425
426fn parse_server_version(info: &[u8]) -> Option<DatabaseVersion> {
427 let info = match info.strip_prefix(MARIADB_REPLICATION_VERSION_PREFIX) {
428 Some(version) if version.first().is_some_and(u8::is_ascii_digit) => version,
429 _ => info,
430 };
431 let end = info
432 .iter()
433 .position(|&byte| !byte.is_ascii_digit() && byte != b'.')
434 .unwrap_or(info.len());
435 core::str::from_utf8(&info[..end]).ok()?.parse().ok()
436}
437
438#[cfg(test)]
439mod tests {
440 use super::*;
441
442 #[diesel_test_helper::test]
443 fn parse_server_version_reads_mysql_and_mariadb_formats() {
444 let v = DatabaseVersion::new;
445 assert_eq!(parse_server_version(b"8.4.11"), Some(v(8, 4, 11)));
446 assert_eq!(
447 parse_server_version(b"8.0.36-0ubuntu0.22.04.1"),
448 Some(v(8, 0, 36))
449 );
450 assert_eq!(
451 parse_server_version(b"11.4.2-MariaDB-ubu2404"),
452 Some(v(11, 4, 2))
453 );
454 assert_eq!(
455 parse_server_version(b"5.5.5-10.11.6-MariaDB-log"),
456 Some(v(10, 11, 6))
457 );
458 assert_eq!(parse_server_version(b"5.5.5-log"), Some(v(5, 5, 5)));
459 assert_eq!(parse_server_version(b"8.4"), None);
460 assert_eq!(parse_server_version(b"8.4.11.2"), None);
461 assert_eq!(parse_server_version(b"8..4"), None);
462 assert_eq!(parse_server_version(b"8..4.11"), None);
463 assert_eq!(parse_server_version(b".8.4.11"), None);
464 assert_eq!(parse_server_version(b"8.4.11."), None);
465 assert_eq!(parse_server_version(b"8.4.4294967296"), None);
466 assert_eq!(parse_server_version(b"MariaDB"), None);
467 }
468}