1use alloc::boxed::Box;
2use core::fmt::{Debug, Display};
3use core::num::NonZeroU32;
4use core::ops::DerefMut;
5use downcast_rs::Downcast;
67#[cfg(feature = "std")]
8static GLOBAL_INSTRUMENTATION: std::sync::RwLock<fn() -> Option<Box<dyn Instrumentation>>> =
9 std::sync::RwLock::new(|| None);
1011/// A helper trait for opaque query representations
12/// which allows to get a `Display` and `Debug`
13/// representation of the underlying type without
14/// exposing type specific details
15pub trait DebugQuery: Debug + Display {}
1617impl<T, DB> DebugQueryfor crate::query_builder::DebugQuery<'_, T, DB> where Self: Debug + Display {}
1819/// A helper type that allows printing out str slices
20///
21/// This type is necessary because it's not possible
22/// to cast from a reference of a unsized type like `&str`
23/// to a reference of a trait object even if that
24/// type implements all necessary traits
25#[doc = " A helper type that allows printing out str slices"]
#[doc = ""]
#[doc = " This type is necessary because it\'s not possible"]
#[doc = " to cast from a reference of a unsized type like `&str`"]
#[doc = " to a reference of a trait object even if that"]
#[doc = " type implements all necessary traits"]
#[non_exhaustive]
pub struct StrQueryHelper<'query> {
s: &'query str,
}#[diesel_derives::__diesel_public_if(
26 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
27)]28pub(crate) struct StrQueryHelper<'query> {
29 s: &'query str,
30}
3132impl<'query> StrQueryHelper<'query> {
33/// Construct a new `StrQueryHelper`
34#[doc = " Construct a new `StrQueryHelper`"]
pub fn new(s: &'query str) -> Self { Self { s } }#[diesel_derives::__diesel_public_if(
35 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
36)]37 #[cfg(any(
38 feature = "postgres",
39 feature = "__sqlite-shared",
40 feature = "mysql",
41 feature = "mariadb",
42 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
43))]
44pub(crate) fn new(s: &'query str) -> Self {
45Self { s }
46 }
47}
4849impl Debugfor StrQueryHelper<'_> {
50fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
51 Debug::fmt(self.s, f)
52 }
53}
5455impl Displayfor StrQueryHelper<'_> {
56fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
57 Display::fmt(&self.s, f)
58 }
59}
6061impl DebugQueryfor StrQueryHelper<'_> {}
6263/// This enum describes possible connection events
64/// that can be handled by an [`Instrumentation`] implementation
65///
66/// Some fields might contain sensitive information, like login
67/// details for the database.
68///
69/// Diesel does not guarantee that future versions will
70/// emit the same events in the same order or timing.
71/// In addition the output of the [`Debug`] and [`Display`]
72/// implementation of the enum itself and any of its fields
73/// is not guarantee to be stable.
74//
75// This type is carefully designed
76// to avoid any potential overhead by
77// taking references for all things
78// and by not performing any additional
79// work until required.
80// In addition it's carefully designed
81// not to be dependent on the actual backend
82// type, as that makes it easier to reuse
83// `Instrumentation` implementations in
84// a different context
85#[derive(#[automatically_derived]
impl<'a> ::core::fmt::Debug for InstrumentationEvent<'a> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
InstrumentationEvent::StartEstablishConnection { url: __self_0 }
=>
::core::fmt::Formatter::debug_struct_field1_finish(f,
"StartEstablishConnection", "url", &__self_0),
InstrumentationEvent::FinishEstablishConnection {
url: __self_0, error: __self_1 } =>
::core::fmt::Formatter::debug_struct_field2_finish(f,
"FinishEstablishConnection", "url", __self_0, "error",
&__self_1),
InstrumentationEvent::StartQuery { query: __self_0 } =>
::core::fmt::Formatter::debug_struct_field1_finish(f,
"StartQuery", "query", &__self_0),
InstrumentationEvent::CacheQuery { sql: __self_0 } =>
::core::fmt::Formatter::debug_struct_field1_finish(f,
"CacheQuery", "sql", &__self_0),
InstrumentationEvent::FinishQuery {
query: __self_0, error: __self_1 } =>
::core::fmt::Formatter::debug_struct_field2_finish(f,
"FinishQuery", "query", __self_0, "error", &__self_1),
InstrumentationEvent::BeginTransaction { depth: __self_0 } =>
::core::fmt::Formatter::debug_struct_field1_finish(f,
"BeginTransaction", "depth", &__self_0),
InstrumentationEvent::CommitTransaction { depth: __self_0 } =>
::core::fmt::Formatter::debug_struct_field1_finish(f,
"CommitTransaction", "depth", &__self_0),
InstrumentationEvent::RollbackTransaction { depth: __self_0 } =>
::core::fmt::Formatter::debug_struct_field1_finish(f,
"RollbackTransaction", "depth", &__self_0),
}
}
}Debug)]
86#[non_exhaustive]
87pub enum InstrumentationEvent<'a> {
88/// An event emitted by before starting
89 /// establishing a new connection
90#[non_exhaustive]
91StartEstablishConnection {
92/// The database url the connection
93 /// tries to connect to
94 ///
95 /// This might contain sensitive information
96 /// like the database password
97url: &'a str,
98 },
99/// An event emitted after establishing a
100 /// new connection
101#[non_exhaustive]
102FinishEstablishConnection {
103/// The database url the connection
104 /// tries is connected to
105 ///
106 /// This might contain sensitive information
107 /// like the database password
108url: &'a str,
109/// An optional error if the connection failed
110error: Option<&'a crate::result::ConnectionError>,
111 },
112/// An event that is emitted before executing
113 /// a query
114#[non_exhaustive]
115StartQuery {
116/// A opaque representation of the query
117 ///
118 /// This type implements [`Debug`] and [`Display`],
119 /// but should be considered otherwise as opaque.
120 ///
121 /// The exact output of the [`Debug`] and [`Display`]
122 /// implementation is not considered as part of the
123 /// stable API.
124query: &'a dyn DebugQuery,
125 },
126/// An event that is emitted when a query
127 /// is cached in the connection internal
128 /// prepared statement cache
129#[non_exhaustive]
130CacheQuery {
131/// SQL string of the cached query
132sql: &'a str,
133 },
134/// An event that is emitted after executing
135 /// a query
136#[non_exhaustive]
137FinishQuery {
138/// A opaque representation of the query
139 ///
140 /// This type implements [`Debug`] and [`Display`],
141 /// but should be considered otherwise as opaque.
142 ///
143 /// The exact output of the [`Debug`] and [`Display`]
144 /// implementation is not considered as part of the
145 /// stable API.
146query: &'a dyn DebugQuery,
147/// An optional error if the connection failed
148error: Option<&'a crate::result::Error>,
149 },
150/// An event that is emitted while
151 /// starting a new transaction
152#[non_exhaustive]
153BeginTransaction {
154/// Transaction level of the newly started
155 /// transaction
156depth: NonZeroU32,
157 },
158/// An event that is emitted while
159 /// committing a transaction
160#[non_exhaustive]
161CommitTransaction {
162/// Transaction level of the to be committed
163 /// transaction
164depth: NonZeroU32,
165 },
166/// An event that is emitted while
167 /// rolling back a transaction
168#[non_exhaustive]
169RollbackTransaction {
170/// Transaction level of the to be rolled
171 /// back transaction
172depth: NonZeroU32,
173 },
174}
175176// these constructors exist to
177// keep `#[non_exhaustive]` on all the variants
178// and to gate the constructors on the unstable feature
179#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
180impl<'a> InstrumentationEvent<'a> {
181/// Create a new `InstrumentationEvent::StartEstablishConnection` event
182#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
183pub fn start_establish_connection(url: &'a str) -> Self {
184Self::StartEstablishConnection { url }
185 }
186187/// Create a new `InstrumentationEvent::FinishEstablishConnection` event
188#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
189pub fn finish_establish_connection(
190 url: &'a str,
191 error: Option<&'a crate::result::ConnectionError>,
192 ) -> Self {
193Self::FinishEstablishConnection { url, error }
194 }
195196/// Create a new `InstrumentationEvent::StartQuery` event
197#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
198pub fn start_query(query: &'a dyn DebugQuery) -> Self {
199Self::StartQuery { query }
200 }
201202/// Create a new `InstrumentationEvent::CacheQuery` event
203#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
204pub fn cache_query(sql: &'a str) -> Self {
205Self::CacheQuery { sql }
206 }
207208/// Create a new `InstrumentationEvent::FinishQuery` event
209#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
210pub fn finish_query(
211 query: &'a dyn DebugQuery,
212 error: Option<&'a crate::result::Error>,
213 ) -> Self {
214Self::FinishQuery { query, error }
215 }
216217/// Create a new `InstrumentationEvent::BeginTransaction` event
218#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
219pub fn begin_transaction(depth: NonZeroU32) -> Self {
220Self::BeginTransaction { depth }
221 }
222223/// Create a new `InstrumentationEvent::RollbackTransaction` event
224#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
225pub fn rollback_transaction(depth: NonZeroU32) -> Self {
226Self::RollbackTransaction { depth }
227 }
228229/// Create a new `InstrumentationEvent::CommitTransaction` event
230#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
231pub fn commit_transaction(depth: NonZeroU32) -> Self {
232Self::CommitTransaction { depth }
233 }
234}
235236/// A type that provides an connection `Instrumentation`
237///
238/// This trait is the basic building block for logging or
239/// otherwise instrumenting diesel connection types. It
240/// acts as callback that receives information about certain
241/// important connection states
242///
243/// For simple usages this trait is implemented for closures
244/// accepting a [`InstrumentationEvent`] as argument.
245///
246/// More complex usages and integrations with frameworks like
247/// `tracing` and `log` are supposed to be part of their own
248/// crates.
249pub trait Instrumentation: Downcast + Send + 'static {
250/// The function that is invoked for each event
251fn on_connection_event(&mut self, event: InstrumentationEvent<'_>);
252}
253impl dyn Instrumentation<> {
/// Returns true if the trait object wraps an object of type `__T`.
#[inline]
pub fn is<__T: Instrumentation<>>(&self) -> bool {
::downcast_rs::Downcast::as_any(self).is::<__T>()
}
/// Returns a boxed object from a boxed trait object if the underlying object is of type
/// `__T`. Returns the original boxed trait if it isn't.
#[inline]
pub fn downcast<__T: Instrumentation<>>(self:
::downcast_rs::__alloc::boxed::Box<Self>)
->
::downcast_rs::__std::result::Result<::downcast_rs::__alloc::boxed::Box<__T>,
::downcast_rs::__alloc::boxed::Box<Self>> {
if self.is::<__T>() {
Ok(::downcast_rs::Downcast::into_any(self).downcast::<__T>().unwrap())
} else { Err(self) }
}
/// Returns an `Rc`-ed object from an `Rc`-ed trait object if the underlying object is of
/// type `__T`. Returns the original `Rc`-ed trait if it isn't.
#[inline]
pub fn downcast_rc<__T: Instrumentation<>>(self:
::downcast_rs::__alloc::rc::Rc<Self>)
->
::downcast_rs::__std::result::Result<::downcast_rs::__alloc::rc::Rc<__T>,
::downcast_rs::__alloc::rc::Rc<Self>> {
if self.is::<__T>() {
Ok(::downcast_rs::Downcast::into_any_rc(self).downcast::<__T>().unwrap())
} else { Err(self) }
}
/// Returns a reference to the object within the trait object if it is of type `__T`, or
/// `None` if it isn't.
#[inline]
pub fn downcast_ref<__T: Instrumentation<>>(&self)
-> ::downcast_rs::__std::option::Option<&__T> {
::downcast_rs::Downcast::as_any(self).downcast_ref::<__T>()
}
/// Returns a mutable reference to the object within the trait object if it is of type
/// `__T`, or `None` if it isn't.
#[inline]
pub fn downcast_mut<__T: Instrumentation<>>(&mut self)
-> ::downcast_rs::__std::option::Option<&mut __T> {
::downcast_rs::Downcast::as_any_mut(self).downcast_mut::<__T>()
}
}downcast_rs::impl_downcast!(Instrumentation);
254255/// Get an instance of the default [`Instrumentation`]
256///
257/// This function is mostly useful for crates implementing
258/// their own connection types
259pub fn get_default_instrumentation() -> Option<Box<dyn Instrumentation>> {
260#[cfg(feature = "std")]
261match GLOBAL_INSTRUMENTATION.read() {
262Ok(f) => (*f)(),
263Err(_) => None,
264 }
265#[cfg(not(feature = "std"))]
266None
267}
268269/// Set a custom constructor for the default [`Instrumentation`]
270/// used by new connections
271///
272/// ```rust
273/// use diesel::connection::{set_default_instrumentation, Instrumentation, InstrumentationEvent};
274///
275/// // a simple logger that prints all events to stdout
276/// fn simple_logger() -> Option<Box<dyn Instrumentation>> {
277/// // we need the explicit argument type there due
278/// // to bugs in rustc
279/// Some(Box::new(|event: InstrumentationEvent<'_>| {
280/// println!("{event:?}")
281/// }))
282/// }
283///
284/// set_default_instrumentation(simple_logger);
285/// ```
286#[cfg(feature = "std")]
287pub fn set_default_instrumentation(
288 default: fn() -> Option<Box<dyn Instrumentation>>,
289) -> crate::QueryResult<()> {
290match GLOBAL_INSTRUMENTATION.write() {
291Ok(mut l) => {
292*l = default;
293Ok(())
294 }
295Err(e) => Err(crate::result::Error::DatabaseError(
296crate::result::DatabaseErrorKind::Unknown,
297Box::new(e.to_string()),
298 )),
299 }
300}
301302impl<F> Instrumentationfor F
303where
304F: FnMut(InstrumentationEvent<'_>) + Send + 'static,
305{
306fn on_connection_event(&mut self, event: InstrumentationEvent<'_>) {
307 (self)(event)
308 }
309}
310311impl Instrumentationfor Box<dyn Instrumentation> {
312fn on_connection_event(&mut self, event: InstrumentationEvent<'_>) {
313self.deref_mut().on_connection_event(event)
314 }
315}
316317impl<T> Instrumentationfor Option<T>
318where
319T: Instrumentation,
320{
321fn on_connection_event(&mut self, event: InstrumentationEvent<'_>) {
322if let Some(i) = self {
323i.on_connection_event(event)
324 }
325 }
326}
327328#[doc = " An optional dyn instrumentation."]
#[doc = ""]
#[doc =
" For ease of use, this type implements [`Deref`] and [`DerefMut`] to `&dyn Instrumentation`,"]
#[doc =
" falling back to a no-op implementation if no instrumentation is set."]
#[doc = ""]
#[doc =
" The DynInstrumentation type is useful because without it we actually did tend to return"]
#[doc =
" (accidentally) `&mut Option<Box> as &mut dyn Instrumentation` from `connection.instrumentation()`,"]
#[doc =
" so downcasting would have to be done in these two steps by the user, which is counter-intuitive."]
#[non_exhaustive]
pub struct DynInstrumentation {
#[doc = " zst"]
no_instrumentation: NoInstrumentation,
inner: Option<Box<dyn Instrumentation>>,
}#[diesel_derives::__diesel_public_if(
329 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
330)]331#[cfg(any(
332 feature = "postgres",
333 feature = "__sqlite-shared",
334 feature = "mysql",
335 feature = "mariadb",
336 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
337))]
338/// An optional dyn instrumentation.
339///
340/// For ease of use, this type implements [`Deref`] and [`DerefMut`] to `&dyn Instrumentation`,
341/// falling back to a no-op implementation if no instrumentation is set.
342///
343/// The DynInstrumentation type is useful because without it we actually did tend to return
344/// (accidentally) `&mut Option<Box> as &mut dyn Instrumentation` from `connection.instrumentation()`,
345/// so downcasting would have to be done in these two steps by the user, which is counter-intuitive.
346pub(crate) struct DynInstrumentation {
347/// zst
348no_instrumentation: NoInstrumentation,
349 inner: Option<Box<dyn Instrumentation>>,
350}
351352#[cfg(any(
353 feature = "postgres",
354 feature = "__sqlite-shared",
355 feature = "mysql",
356 feature = "mariadb",
357 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
358))]
359impl core::ops::Dereffor DynInstrumentation {
360type Target = dyn Instrumentation;
361362fn deref(&self) -> &Self::Target {
363self.inner.as_deref().unwrap_or(&self.no_instrumentation)
364 }
365}
366367#[cfg(any(
368 feature = "postgres",
369 feature = "__sqlite-shared",
370 feature = "mysql",
371 feature = "mariadb",
372 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
373))]
374impl DerefMutfor DynInstrumentation {
375fn deref_mut(&mut self) -> &mut Self::Target {
376self.inner
377 .as_deref_mut()
378 .unwrap_or(&mut self.no_instrumentation)
379 }
380}
381382#[cfg(any(
383 feature = "postgres",
384 feature = "__sqlite-shared",
385 feature = "mysql",
386 feature = "mariadb",
387 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
388))]
389impl DynInstrumentation {
390/// Create a instance of the default instrumentation provider
391#[doc = " Create a instance of the default instrumentation provider"]
pub fn default_instrumentation() -> Self {
Self {
inner: get_default_instrumentation(),
no_instrumentation: NoInstrumentation,
}
}#[diesel_derives::__diesel_public_if(
392 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
393)]394 #[cfg(any(
395 feature = "postgres",
396 feature = "__sqlite-shared",
397 feature = "mysql",
398 feature = "mariadb",
399 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
400))]
401pub(crate) fn default_instrumentation() -> Self {
402Self {
403 inner: get_default_instrumentation(),
404 no_instrumentation: NoInstrumentation,
405 }
406 }
407408/// Create a noop instrumentation provider instance
409#[doc = " Create a noop instrumentation provider instance"]
pub fn none() -> Self {
Self { inner: None, no_instrumentation: NoInstrumentation }
}#[diesel_derives::__diesel_public_if(
410 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
411)]412 #[cfg(any(
413 feature = "postgres",
414 feature = "__sqlite-shared",
415 feature = "mysql",
416 feature = "mariadb",
417 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
418))]
419pub(crate) fn none() -> Self {
420Self {
421 inner: None,
422 no_instrumentation: NoInstrumentation,
423 }
424 }
425426/// register an event with the given instrumentation implementation
427#[doc = " register an event with the given instrumentation implementation"]
pub fn on_connection_event(&mut self, event: InstrumentationEvent<'_>) {
if let Some(inner) = self.inner.as_deref_mut() {
inner.on_connection_event(event)
}
}#[diesel_derives::__diesel_public_if(
428 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
429)]430 #[cfg(any(
431 feature = "postgres",
432 feature = "__sqlite-shared",
433 feature = "mysql",
434 feature = "mariadb",
435 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
436))]
437pub(crate) fn on_connection_event(&mut self, event: InstrumentationEvent<'_>) {
438// This implementation is not necessary to be able to call this method on this object
439 // because of the already existing Deref impl.
440 // However it allows avoiding the dynamic dispatch to the stub value
441if let Some(inner) = self.inner.as_deref_mut() {
442inner.on_connection_event(event)
443 }
444 }
445}
446447#[cfg(any(
448 feature = "postgres",
449 feature = "__sqlite-shared",
450 feature = "mysql",
451 feature = "mariadb",
452 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
453))]
454impl<I: Instrumentation> From<I> for DynInstrumentation {
455fn from(instrumentation: I) -> Self {
456Self {
457 inner: Some(unpack_instrumentation(Box::new(instrumentation))),
458 no_instrumentation: NoInstrumentation,
459 }
460 }
461}
462#[cfg(any(
463 feature = "postgres",
464 feature = "__sqlite-shared",
465 feature = "mysql",
466 feature = "mariadb",
467 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
468))]
469struct NoInstrumentation;
470471#[cfg(any(
472 feature = "postgres",
473 feature = "__sqlite-shared",
474 feature = "mysql",
475 feature = "mariadb",
476 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
477))]
478impl Instrumentationfor NoInstrumentation {
479fn on_connection_event(&mut self, _: InstrumentationEvent<'_>) {}
480}
481482/// Unwrap unnecessary boxing levels
483#[cfg(any(
484 feature = "postgres",
485 feature = "__sqlite-shared",
486 feature = "mysql",
487 feature = "mariadb",
488 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
489))]
490fn unpack_instrumentation(
491mut instrumentation: Box<dyn Instrumentation>,
492) -> Box<dyn Instrumentation> {
493loop {
494match instrumentation.downcast::<Box<dyn Instrumentation>>() {
495Ok(extra_boxed_instrumentation) => instrumentation = *extra_boxed_instrumentation,
496Err(not_extra_boxed_instrumentation) => {
497break not_extra_boxed_instrumentation;
498 }
499 }
500 }
501}