diesel/query_builder/
debug_query.rs

1use std::fmt::{self, Debug, Display};
2use std::marker::PhantomData;
3use std::mem;
4
5use super::{AstPass, QueryBuilder, QueryFragment};
6use backend::Backend;
7
8/// A struct that implements `fmt::Display` and `fmt::Debug` to show the SQL
9/// representation of a query.
10///
11/// The `Display` implementation will be the exact query sent to the server,
12/// plus a comment with the values of the bind parameters. The `Debug`
13/// implementation is more structured, and able to be pretty printed.
14///
15/// See [`debug_query`] for usage examples.
16///
17/// [`debug_query`]: ../fn.debug_query.html
18pub struct DebugQuery<'a, T: 'a, DB> {
19    query: &'a T,
20    _marker: PhantomData<DB>,
21}
22
23impl<'a, T, DB> DebugQuery<'a, T, DB> {
24    pub(crate) fn new(query: &'a T) -> Self {
25        DebugQuery {
26            query,
27            _marker: PhantomData,
28        }
29    }
30}
31
32impl<'a, T, DB> Display for DebugQuery<'a, T, DB>
33where
34    DB: Backend,
35    DB::QueryBuilder: Default,
36    T: QueryFragment<DB>,
37{
38    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
39        let mut query_builder = DB::QueryBuilder::default();
40        QueryFragment::<DB>::to_sql(self.query, &mut query_builder).map_err(|_| fmt::Error)?;
41        let debug_binds = DebugBinds::<_, DB>::new(self.query);
42        write!(f, "{} -- binds: {:?}", query_builder.finish(), debug_binds)
43    }
44}
45
46impl<'a, T, DB> Debug for DebugQuery<'a, T, DB>
47where
48    DB: Backend,
49    DB::QueryBuilder: Default,
50    T: QueryFragment<DB>,
51{
52    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
53        let mut query_builder = DB::QueryBuilder::default();
54        QueryFragment::<DB>::to_sql(self.query, &mut query_builder).map_err(|_| fmt::Error)?;
55        let debug_binds = DebugBinds::<_, DB>::new(self.query);
56        f.debug_struct("Query")
57            .field("sql", &query_builder.finish())
58            .field("binds", &debug_binds)
59            .finish()
60    }
61}
62
63/// A struct that implements `fmt::Debug` by walking the given AST and writing
64/// the `fmt::Debug` implementation of each bind parameter.
65pub struct DebugBinds<'a, T: 'a, DB> {
66    query: &'a T,
67    _marker: PhantomData<DB>,
68}
69
70impl<'a, T, DB> DebugBinds<'a, T, DB> {
71    fn new(query: &'a T) -> Self {
72        DebugBinds {
73            query,
74            _marker: PhantomData,
75        }
76    }
77}
78
79impl<'a, T, DB> Debug for DebugBinds<'a, T, DB>
80where
81    DB: Backend,
82    T: QueryFragment<DB>,
83{
84    // Clippy is wrong, this cannot be expressed with pointer casting
85    #[allow(clippy::transmute_ptr_to_ptr)]
86    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
87        let mut list = f.debug_list();
88        {
89            // Safe because the lifetime is shortened to one smaller
90            // than the lifetime of the formatter.
91            let list_with_shorter_lifetime = unsafe { mem::transmute(&mut list) };
92            let ast_pass = AstPass::debug_binds(list_with_shorter_lifetime);
93            self.query.walk_ast(ast_pass).map_err(|_| fmt::Error)?;
94        }
95        list.finish()?;
96        Ok(())
97    }
98}