1use crate::connection::LogSettings;
2#[cfg(feature = "sqlite")]
3use std::collections::HashSet;
4#[cfg(feature = "sqlite")]
5use std::fmt::Debug;
6#[cfg(feature = "sqlite")]
7use std::hash::Hash;
8use std::time::Instant;
9
10pub(crate) struct QueryLogger<'q> {
11 sql: &'q str,
12 rows_returned: u64,
13 rows_affected: u64,
14 start: Instant,
15 settings: LogSettings,
16}
17
18impl<'q> QueryLogger<'q> {
19 pub(crate) fn new(sql: &'q str, settings: LogSettings) -> Self {
20 Self {
21 sql,
22 rows_returned: 0,
23 rows_affected: 0,
24 start: Instant::now(),
25 settings,
26 }
27 }
28
29 pub(crate) fn increment_rows_returned(&mut self) {
30 self.rows_returned += 1;
31 }
32
33 pub(crate) fn increase_rows_affected(&mut self, n: u64) {
34 self.rows_affected += n;
35 }
36
37 pub(crate) fn finish(&self) {
38 let elapsed = self.start.elapsed();
39
40 let lvl = if elapsed >= self.settings.slow_statements_duration {
41 self.settings.slow_statements_level
42 } else {
43 self.settings.statements_level
44 };
45
46 if let Some(lvl) = lvl
47 .to_level()
48 .filter(|lvl| log::log_enabled!(target: "sqlx::query", *lvl))
49 {
50 let mut summary = parse_query_summary(&self.sql);
51
52 let sql = if summary != self.sql {
53 summary.push_str(" …");
54 format!(
55 "\n\n{}\n",
56 sqlformat::format(
57 &self.sql,
58 &sqlformat::QueryParams::None,
59 sqlformat::FormatOptions::default()
60 )
61 )
62 } else {
63 String::new()
64 };
65
66 log::logger().log(
67 &log::Record::builder()
68 .args(format_args!(
69 "{}; rows affected: {}, rows returned: {}, elapsed: {:.3?}{}",
70 summary, self.rows_affected, self.rows_returned, elapsed, sql
71 ))
72 .level(lvl)
73 .module_path_static(Some("sqlx::query"))
74 .target("sqlx::query")
75 .build(),
76 );
77 }
78 }
79}
80
81impl<'q> Drop for QueryLogger<'q> {
82 fn drop(&mut self) {
83 self.finish();
84 }
85}
86
87#[cfg(feature = "sqlite")]
88pub(crate) struct QueryPlanLogger<'q, O: Debug + Hash + Eq, R: Debug + Hash + Eq, P: Debug> {
89 sql: &'q str,
90 unknown_operations: HashSet<O>,
91 results: HashSet<R>,
92 program: Vec<P>,
93 settings: LogSettings,
94}
95
96#[cfg(feature = "sqlite")]
97impl<'q, O: Debug + Hash + Eq, R: Debug + Hash + Eq, P: Debug> QueryPlanLogger<'q, O, R, P> {
98 pub(crate) fn new(sql: &'q str, settings: LogSettings) -> Self {
99 Self {
100 sql,
101 unknown_operations: HashSet::new(),
102 results: HashSet::new(),
103 program: Vec::new(),
104 settings,
105 }
106 }
107
108 pub(crate) fn add_result(&mut self, result: R) {
109 self.results.insert(result);
110 }
111
112 pub(crate) fn add_program(&mut self, program: Vec<P>) {
113 self.program = program;
114 }
115
116 pub(crate) fn add_unknown_operation(&mut self, operation: O) {
117 self.unknown_operations.insert(operation);
118 }
119
120 pub(crate) fn finish(&self) {
121 let lvl = self.settings.statements_level;
122
123 if let Some(lvl) = lvl
124 .to_level()
125 .filter(|lvl| log::log_enabled!(target: "sqlx::explain", *lvl))
126 {
127 let mut summary = parse_query_summary(&self.sql);
128
129 let sql = if summary != self.sql {
130 summary.push_str(" …");
131 format!(
132 "\n\n{}\n",
133 sqlformat::format(
134 &self.sql,
135 &sqlformat::QueryParams::None,
136 sqlformat::FormatOptions::default()
137 )
138 )
139 } else {
140 String::new()
141 };
142
143 log::logger().log(
144 &log::Record::builder()
145 .args(format_args!(
146 "{}; program:{:?}, unknown_operations:{:?}, results: {:?}{}",
147 summary, self.program, self.unknown_operations, self.results, sql
148 ))
149 .level(lvl)
150 .module_path_static(Some("sqlx::explain"))
151 .target("sqlx::explain")
152 .build(),
153 );
154 }
155 }
156}
157
158#[cfg(feature = "sqlite")]
159impl<'q, O: Debug + Hash + Eq, R: Debug + Hash + Eq, P: Debug> Drop
160 for QueryPlanLogger<'q, O, R, P>
161{
162 fn drop(&mut self) {
163 self.finish();
164 }
165}
166
167fn parse_query_summary(sql: &str) -> String {
168 sql.split_whitespace()
170 .take(4)
171 .collect::<Vec<&str>>()
172 .join(" ")
173}