diesel/connection/
instrumentation.rs1use downcast_rs::Downcast;
2use std::fmt::{Debug, Display};
3use std::num::NonZeroU32;
4use std::ops::{Deref, DerefMut};
5
6static GLOBAL_INSTRUMENTATION: std::sync::RwLock<fn() -> Option<Box<dyn Instrumentation>>> =
7 std::sync::RwLock::new(|| None);
8
9pub trait DebugQuery: Debug + Display {}
14
15impl<T, DB> DebugQuery for crate::query_builder::DebugQuery<'_, T, DB> where Self: Debug + Display {}
16
17#[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}
29
30impl<'query> StrQueryHelper<'query> {
31 #[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 ))]
41 pub(crate) fn new(s: &'query str) -> Self {
42 Self { s }
43 }
44}
45
46impl Debug for StrQueryHelper<'_> {
47 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48 Debug::fmt(self.s, f)
49 }
50}
51
52impl Display for StrQueryHelper<'_> {
53 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54 Display::fmt(&self.s, f)
55 }
56}
57
58impl DebugQuery for StrQueryHelper<'_> {}
59
60#[derive(Debug)]
83#[non_exhaustive]
84pub enum InstrumentationEvent<'a> {
85 #[non_exhaustive]
88 StartEstablishConnection {
89 url: &'a str,
95 },
96 #[non_exhaustive]
99 FinishEstablishConnection {
100 url: &'a str,
106 error: Option<&'a crate::result::ConnectionError>,
108 },
109 #[non_exhaustive]
112 StartQuery {
113 query: &'a dyn DebugQuery,
122 },
123 #[non_exhaustive]
127 CacheQuery {
128 sql: &'a str,
130 },
131 #[non_exhaustive]
134 FinishQuery {
135 query: &'a dyn DebugQuery,
144 error: Option<&'a crate::result::Error>,
146 },
147 #[non_exhaustive]
150 BeginTransaction {
151 depth: NonZeroU32,
154 },
155 #[non_exhaustive]
158 CommitTransaction {
159 depth: NonZeroU32,
162 },
163 #[non_exhaustive]
166 RollbackTransaction {
167 depth: NonZeroU32,
170 },
171}
172
173#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
177impl<'a> InstrumentationEvent<'a> {
178 #[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
180 pub fn start_establish_connection(url: &'a str) -> Self {
181 Self::StartEstablishConnection { url }
182 }
183
184 #[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
186 pub fn finish_establish_connection(
187 url: &'a str,
188 error: Option<&'a crate::result::ConnectionError>,
189 ) -> Self {
190 Self::FinishEstablishConnection { url, error }
191 }
192
193 #[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
195 pub fn start_query(query: &'a dyn DebugQuery) -> Self {
196 Self::StartQuery { query }
197 }
198
199 #[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
201 pub fn cache_query(sql: &'a str) -> Self {
202 Self::CacheQuery { sql }
203 }
204
205 #[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
207 pub fn finish_query(
208 query: &'a dyn DebugQuery,
209 error: Option<&'a crate::result::Error>,
210 ) -> Self {
211 Self::FinishQuery { query, error }
212 }
213
214 #[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
216 pub fn begin_transaction(depth: NonZeroU32) -> Self {
217 Self::BeginTransaction { depth }
218 }
219
220 #[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
222 pub fn rollback_transaction(depth: NonZeroU32) -> Self {
223 Self::RollbackTransaction { depth }
224 }
225
226 #[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
228 pub fn commit_transaction(depth: NonZeroU32) -> Self {
229 Self::CommitTransaction { depth }
230 }
231}
232
233pub trait Instrumentation: Downcast + Send + 'static {
247 fn on_connection_event(&mut self, event: InstrumentationEvent<'_>);
249}
250downcast_rs::impl_downcast!(Instrumentation);
251
252pub fn get_default_instrumentation() -> Option<Box<dyn Instrumentation>> {
257 match GLOBAL_INSTRUMENTATION.read() {
258 Ok(f) => (*f)(),
259 Err(_) => None,
260 }
261}
262
263pub fn set_default_instrumentation(
281 default: fn() -> Option<Box<dyn Instrumentation>>,
282) -> crate::QueryResult<()> {
283 match GLOBAL_INSTRUMENTATION.write() {
284 Ok(mut l) => {
285 *l = default;
286 Ok(())
287 }
288 Err(e) => Err(crate::result::Error::DatabaseError(
289 crate::result::DatabaseErrorKind::Unknown,
290 Box::new(e.to_string()),
291 )),
292 }
293}
294
295impl<F> Instrumentation for F
296where
297 F: FnMut(InstrumentationEvent<'_>) + Send + 'static,
298{
299 fn on_connection_event(&mut self, event: InstrumentationEvent<'_>) {
300 (self)(event)
301 }
302}
303
304impl Instrumentation for Box<dyn Instrumentation> {
305 fn on_connection_event(&mut self, event: InstrumentationEvent<'_>) {
306 self.deref_mut().on_connection_event(event)
307 }
308}
309
310impl<T> Instrumentation for Option<T>
311where
312 T: Instrumentation,
313{
314 fn on_connection_event(&mut self, event: InstrumentationEvent<'_>) {
315 if let Some(i) = self {
316 i.on_connection_event(event)
317 }
318 }
319}
320
321#[diesel_derives::__diesel_public_if(
322 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
323)]
324pub(crate) struct DynInstrumentation {
333 no_instrumentation: NoInstrumentation,
335 inner: Option<Box<dyn Instrumentation>>,
336}
337
338impl Deref for DynInstrumentation {
339 type Target = dyn Instrumentation;
340
341 fn deref(&self) -> &Self::Target {
342 self.inner.as_deref().unwrap_or(&self.no_instrumentation)
343 }
344}
345
346impl DerefMut for DynInstrumentation {
347 fn deref_mut(&mut self) -> &mut Self::Target {
348 self.inner
349 .as_deref_mut()
350 .unwrap_or(&mut self.no_instrumentation)
351 }
352}
353
354impl DynInstrumentation {
355 #[diesel_derives::__diesel_public_if(
357 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
358 )]
359 pub(crate) fn default_instrumentation() -> Self {
360 Self {
361 inner: get_default_instrumentation(),
362 no_instrumentation: NoInstrumentation,
363 }
364 }
365
366 #[diesel_derives::__diesel_public_if(
368 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
369 )]
370 pub(crate) fn none() -> Self {
371 Self {
372 inner: None,
373 no_instrumentation: NoInstrumentation,
374 }
375 }
376
377 #[diesel_derives::__diesel_public_if(
379 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
380 )]
381 pub(crate) fn on_connection_event(&mut self, event: InstrumentationEvent<'_>) {
382 if let Some(inner) = self.inner.as_deref_mut() {
386 inner.on_connection_event(event)
387 }
388 }
389}
390
391impl<I: Instrumentation> From<I> for DynInstrumentation {
392 fn from(instrumentation: I) -> Self {
393 Self {
394 inner: Some(unpack_instrumentation(Box::new(instrumentation))),
395 no_instrumentation: NoInstrumentation,
396 }
397 }
398}
399
400struct NoInstrumentation;
401
402impl Instrumentation for NoInstrumentation {
403 fn on_connection_event(&mut self, _: InstrumentationEvent<'_>) {}
404}
405
406fn unpack_instrumentation(
408 mut instrumentation: Box<dyn Instrumentation>,
409) -> Box<dyn Instrumentation> {
410 loop {
411 match instrumentation.downcast::<Box<dyn Instrumentation>>() {
412 Ok(extra_boxed_instrumentation) => instrumentation = *extra_boxed_instrumentation,
413 Err(not_extra_boxed_instrumentation) => {
414 break not_extra_boxed_instrumentation;
415 }
416 }
417 }
418}