1use downcast_rs::Downcast;
2use std::fmt::{Debug, Display};
3use std::num::NonZeroU32;
4use std::ops::{Deref, DerefMut};
56static GLOBAL_INSTRUMENTATION: std::sync::RwLock<fn() -> Option<Box<dyn Instrumentation>>> =
7 std::sync::RwLock::new(|| None);
89/// A helper trait for opaque query representations
10/// which allows to get a `Display` and `Debug`
11/// representation of the underlying type without
12/// exposing type specific details
13pub trait DebugQuery: Debug + Display {}
1415impl<T, DB> DebugQueryfor crate::query_builder::DebugQuery<'_, T, DB> where Self: Debug + Display {}
1617/// A helper type that allows printing out str slices
18///
19/// This type is necessary because it's not possible
20/// to cast from a reference of a unsized type like `&str`
21/// to a reference of a trait object even if that
22/// type implements all necessary traits
23#[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(
24 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
25)]26pub(crate) struct StrQueryHelper<'query> {
27 s: &'query str,
28}
2930impl<'query> StrQueryHelper<'query> {
31/// Construct a new `StrQueryHelper`
32#[diesel_derives::__diesel_public_if(
33 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
34)]
35 #[cfg(any(
36 feature = "postgres",
37 feature = "sqlite",
38 feature = "mysql",
39 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
40))]
41pub(crate) fn new(s: &'query str) -> Self {
42Self { s }
43 }
44}
4546impl Debugfor StrQueryHelper<'_> {
47fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48 Debug::fmt(self.s, f)
49 }
50}
5152impl Displayfor StrQueryHelper<'_> {
53fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54 Display::fmt(&self.s, f)
55 }
56}
5758impl DebugQueryfor StrQueryHelper<'_> {}
5960/// This enum describes possible connection events
61/// that can be handled by an [`Instrumentation`] implementation
62///
63/// Some fields might contain sensitive information, like login
64/// details for the database.
65///
66/// Diesel does not guarantee that future versions will
67/// emit the same events in the same order or timing.
68/// In addition the output of the [`Debug`] and [`Display`]
69/// implementation of the enum itself and any of its fields
70/// is not guarantee to be stable.
71//
72// This type is carefully designed
73// to avoid any potential overhead by
74// taking references for all things
75// and by not performing any additional
76// work until required.
77// In addition it's carefully designed
78// not to be dependent on the actual backend
79// type, as that makes it easier to reuse
80// `Instrumentation` implementations in
81// a different context
82#[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)]
83#[non_exhaustive]
84pub enum InstrumentationEvent<'a> {
85/// An event emitted by before starting
86 /// establishing a new connection
87#[non_exhaustive]
88StartEstablishConnection {
89/// The database url the connection
90 /// tries to connect to
91 ///
92 /// This might contain sensitive information
93 /// like the database password
94url: &'a str,
95 },
96/// An event emitted after establishing a
97 /// new connection
98#[non_exhaustive]
99FinishEstablishConnection {
100/// The database url the connection
101 /// tries is connected to
102 ///
103 /// This might contain sensitive information
104 /// like the database password
105url: &'a str,
106/// An optional error if the connection failed
107error: Option<&'a crate::result::ConnectionError>,
108 },
109/// An event that is emitted before executing
110 /// a query
111#[non_exhaustive]
112StartQuery {
113/// A opaque representation of the query
114 ///
115 /// This type implements [`Debug`] and [`Display`],
116 /// but should be considered otherwise as opaque.
117 ///
118 /// The exact output of the [`Debug`] and [`Display`]
119 /// implementation is not considered as part of the
120 /// stable API.
121query: &'a dyn DebugQuery,
122 },
123/// An event that is emitted when a query
124 /// is cached in the connection internal
125 /// prepared statement cache
126#[non_exhaustive]
127CacheQuery {
128/// SQL string of the cached query
129sql: &'a str,
130 },
131/// An event that is emitted after executing
132 /// a query
133#[non_exhaustive]
134FinishQuery {
135/// A opaque representation of the query
136 ///
137 /// This type implements [`Debug`] and [`Display`],
138 /// but should be considered otherwise as opaque.
139 ///
140 /// The exact output of the [`Debug`] and [`Display`]
141 /// implementation is not considered as part of the
142 /// stable API.
143query: &'a dyn DebugQuery,
144/// An optional error if the connection failed
145error: Option<&'a crate::result::Error>,
146 },
147/// An event that is emitted while
148 /// starting a new transaction
149#[non_exhaustive]
150BeginTransaction {
151/// Transaction level of the newly started
152 /// transaction
153depth: NonZeroU32,
154 },
155/// An event that is emitted while
156 /// committing a transaction
157#[non_exhaustive]
158CommitTransaction {
159/// Transaction level of the to be committed
160 /// transaction
161depth: NonZeroU32,
162 },
163/// An event that is emitted while
164 /// rolling back a transaction
165#[non_exhaustive]
166RollbackTransaction {
167/// Transaction level of the to be rolled
168 /// back transaction
169depth: NonZeroU32,
170 },
171}
172173// these constructors exist to
174// keep `#[non_exhaustive]` on all the variants
175// and to gate the constructors on the unstable feature
176#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
177impl<'a> InstrumentationEvent<'a> {
178/// Create a new `InstrumentationEvent::StartEstablishConnection` event
179#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
180pub fn start_establish_connection(url: &'a str) -> Self {
181Self::StartEstablishConnection { url }
182 }
183184/// Create a new `InstrumentationEvent::FinishEstablishConnection` event
185#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
186pub fn finish_establish_connection(
187 url: &'a str,
188 error: Option<&'a crate::result::ConnectionError>,
189 ) -> Self {
190Self::FinishEstablishConnection { url, error }
191 }
192193/// Create a new `InstrumentationEvent::StartQuery` event
194#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
195pub fn start_query(query: &'a dyn DebugQuery) -> Self {
196Self::StartQuery { query }
197 }
198199/// Create a new `InstrumentationEvent::CacheQuery` event
200#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
201pub fn cache_query(sql: &'a str) -> Self {
202Self::CacheQuery { sql }
203 }
204205/// Create a new `InstrumentationEvent::FinishQuery` event
206#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
207pub fn finish_query(
208 query: &'a dyn DebugQuery,
209 error: Option<&'a crate::result::Error>,
210 ) -> Self {
211Self::FinishQuery { query, error }
212 }
213214/// Create a new `InstrumentationEvent::BeginTransaction` event
215#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
216pub fn begin_transaction(depth: NonZeroU32) -> Self {
217Self::BeginTransaction { depth }
218 }
219220/// Create a new `InstrumentationEvent::RollbackTransaction` event
221#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
222pub fn rollback_transaction(depth: NonZeroU32) -> Self {
223Self::RollbackTransaction { depth }
224 }
225226/// Create a new `InstrumentationEvent::CommitTransaction` event
227#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
228pub fn commit_transaction(depth: NonZeroU32) -> Self {
229Self::CommitTransaction { depth }
230 }
231}
232233/// A type that provides an connection `Instrumentation`
234///
235/// This trait is the basic building block for logging or
236/// otherwise instrumenting diesel connection types. It
237/// acts as callback that receives information about certain
238/// important connection states
239///
240/// For simple usages this trait is implemented for closures
241/// accepting a [`InstrumentationEvent`] as argument.
242///
243/// More complex usages and integrations with frameworks like
244/// `tracing` and `log` are supposed to be part of their own
245/// crates.
246pub trait Instrumentation: Downcast + Send + 'static {
247/// The function that is invoked for each event
248fn on_connection_event(&mut self, event: InstrumentationEvent<'_>);
249}
250impl 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);
251252/// Get an instance of the default [`Instrumentation`]
253///
254/// This function is mostly useful for crates implementing
255/// their own connection types
256pub fn get_default_instrumentation() -> Option<Box<dyn Instrumentation>> {
257match GLOBAL_INSTRUMENTATION.read() {
258Ok(f) => (*f)(),
259Err(_) => None,
260 }
261}
262263/// Set a custom constructor for the default [`Instrumentation`]
264/// used by new connections
265///
266/// ```rust
267/// use diesel::connection::{set_default_instrumentation, Instrumentation, InstrumentationEvent};
268///
269/// // a simple logger that prints all events to stdout
270/// fn simple_logger() -> Option<Box<dyn Instrumentation>> {
271/// // we need the explicit argument type there due
272/// // to bugs in rustc
273/// Some(Box::new(|event: InstrumentationEvent<'_>| {
274/// println!("{event:?}")
275/// }))
276/// }
277///
278/// set_default_instrumentation(simple_logger);
279/// ```
280pub fn set_default_instrumentation(
281 default: fn() -> Option<Box<dyn Instrumentation>>,
282) -> crate::QueryResult<()> {
283match GLOBAL_INSTRUMENTATION.write() {
284Ok(mut l) => {
285*l = default;
286Ok(())
287 }
288Err(e) => Err(crate::result::Error::DatabaseError(
289crate::result::DatabaseErrorKind::Unknown,
290Box::new(e.to_string()),
291 )),
292 }
293}
294295impl<F> Instrumentationfor F
296where
297F: FnMut(InstrumentationEvent<'_>) + Send + 'static,
298{
299fn on_connection_event(&mut self, event: InstrumentationEvent<'_>) {
300 (self)(event)
301 }
302}
303304impl Instrumentationfor Box<dyn Instrumentation> {
305fn on_connection_event(&mut self, event: InstrumentationEvent<'_>) {
306self.deref_mut().on_connection_event(event)
307 }
308}
309310impl<T> Instrumentationfor Option<T>
311where
312T: Instrumentation,
313{
314fn on_connection_event(&mut self, event: InstrumentationEvent<'_>) {
315if let Some(i) = self {
316i.on_connection_event(event)
317 }
318 }
319}
320321#[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(
322 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
323)]324/// An optional dyn instrumentation.
325///
326/// For ease of use, this type implements [`Deref`] and [`DerefMut`] to `&dyn Instrumentation`,
327/// falling back to a no-op implementation if no instrumentation is set.
328///
329/// The DynInstrumentation type is useful because without it we actually did tend to return
330/// (accidentally) `&mut Option<Box> as &mut dyn Instrumentation` from `connection.instrumentation()`,
331/// so downcasting would have to be done in these two steps by the user, which is counter-intuitive.
332pub(crate) struct DynInstrumentation {
333/// zst
334no_instrumentation: NoInstrumentation,
335 inner: Option<Box<dyn Instrumentation>>,
336}
337338impl Dereffor DynInstrumentation {
339type Target = dyn Instrumentation;
340341fn deref(&self) -> &Self::Target {
342self.inner.as_deref().unwrap_or(&self.no_instrumentation)
343 }
344}
345346impl DerefMutfor DynInstrumentation {
347fn deref_mut(&mut self) -> &mut Self::Target {
348self.inner
349 .as_deref_mut()
350 .unwrap_or(&mut self.no_instrumentation)
351 }
352}
353354impl DynInstrumentation {
355/// Create a instance of the default instrumentation provider
356#[diesel_derives::__diesel_public_if(
357 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
358)]
359 #[cfg(any(
360 feature = "postgres",
361 feature = "sqlite",
362 feature = "mysql",
363 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
364))]
365pub(crate) fn default_instrumentation() -> Self {
366Self {
367 inner: get_default_instrumentation(),
368 no_instrumentation: NoInstrumentation,
369 }
370 }
371372/// Create a noop instrumentation provider instance
373#[diesel_derives::__diesel_public_if(
374 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
375)]
376 #[cfg(any(
377 feature = "postgres",
378 feature = "sqlite",
379 feature = "mysql",
380 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
381))]
382pub(crate) fn none() -> Self {
383Self {
384 inner: None,
385 no_instrumentation: NoInstrumentation,
386 }
387 }
388389/// register an event with the given instrumentation implementation
390#[diesel_derives::__diesel_public_if(
391 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
392)]
393 #[cfg(any(
394 feature = "postgres",
395 feature = "sqlite",
396 feature = "mysql",
397 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
398))]
399pub(crate) fn on_connection_event(&mut self, event: InstrumentationEvent<'_>) {
400// This implementation is not necessary to be able to call this method on this object
401 // because of the already existing Deref impl.
402 // However it allows avoiding the dynamic dispatch to the stub value
403if let Some(inner) = self.inner.as_deref_mut() {
404inner.on_connection_event(event)
405 }
406 }
407}
408409impl<I: Instrumentation> From<I> for DynInstrumentation {
410fn from(instrumentation: I) -> Self {
411Self {
412 inner: Some(unpack_instrumentation(Box::new(instrumentation))),
413 no_instrumentation: NoInstrumentation,
414 }
415 }
416}
417418struct NoInstrumentation;
419420impl Instrumentationfor NoInstrumentation {
421fn on_connection_event(&mut self, _: InstrumentationEvent<'_>) {}
422}
423424/// Unwrap unnecessary boxing levels
425fn unpack_instrumentation(
426mut instrumentation: Box<dyn Instrumentation>,
427) -> Box<dyn Instrumentation> {
428loop {
429match instrumentation.downcast::<Box<dyn Instrumentation>>() {
430Ok(extra_boxed_instrumentation) => instrumentation = *extra_boxed_instrumentation,
431Err(not_extra_boxed_instrumentation) => {
432break not_extra_boxed_instrumentation;
433 }
434 }
435 }
436}