1#[cfg(feature = "backend-mysql")]
2use crate::extension::mysql::{ExplainTableTarget, MySqlExplainOptions, MySqlExplainSchemaSpec};
3#[cfg(feature = "backend-postgres")]
4use crate::extension::postgres::{PgExplainOptions, PgExplainSerialize};
5#[cfg(feature = "backend-sqlite")]
6use crate::extension::sqlite::SqliteExplainOptions;
7use crate::{
8 DeleteStatement, InsertStatement, QueryBuilder, QueryStatement, SelectStatement, SqlWriter,
9 SqlWriterValues, UpdateStatement, Values,
10};
11
12#[cfg(feature = "backend-mysql")]
13use crate::{IntoIden, IntoTableRef};
14
15#[cfg(any(feature = "backend-postgres", feature = "backend-mysql"))]
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17#[non_exhaustive]
18pub enum ExplainFormat {
19 #[cfg(feature = "backend-postgres")]
20 Text,
21 #[cfg(feature = "backend-postgres")]
22 Xml,
23 #[cfg(any(feature = "backend-postgres", feature = "backend-mysql"))]
24 Json,
25 #[cfg(feature = "backend-postgres")]
26 Yaml,
27 #[cfg(feature = "backend-mysql")]
28 Tree,
29 #[cfg(feature = "backend-mysql")]
30 Traditional,
31}
32
33#[cfg(any(feature = "backend-postgres", feature = "backend-mysql"))]
34impl ExplainFormat {
35 pub fn as_str(&self) -> &'static str {
36 match self {
37 #[cfg(feature = "backend-postgres")]
38 Self::Text => "TEXT",
39 #[cfg(feature = "backend-postgres")]
40 Self::Xml => "XML",
41 #[cfg(any(feature = "backend-postgres", feature = "backend-mysql"))]
42 Self::Json => "JSON",
43 #[cfg(feature = "backend-postgres")]
44 Self::Yaml => "YAML",
45 #[cfg(feature = "backend-mysql")]
46 Self::Tree => "TREE",
47 #[cfg(feature = "backend-mysql")]
48 Self::Traditional => "TRADITIONAL",
49 }
50 }
51}
52
53#[derive(Debug, Clone, PartialEq)]
54#[non_exhaustive]
55pub enum ExplainableStatement {
56 Select(SelectStatement),
57 Insert(InsertStatement),
58 Update(UpdateStatement),
59 Delete(DeleteStatement),
60}
61
62impl ExplainableStatement {
63 pub(crate) fn write_to(&self, query_builder: &impl QueryBuilder, sql: &mut impl SqlWriter) {
64 match self {
65 Self::Select(statement) => query_builder.prepare_select_statement(statement, sql),
66 Self::Insert(statement) => query_builder.prepare_insert_statement(statement, sql),
67 Self::Update(statement) => query_builder.prepare_update_statement(statement, sql),
68 Self::Delete(statement) => query_builder.prepare_delete_statement(statement, sql),
69 }
70 }
71}
72
73impl From<QueryStatement> for ExplainableStatement {
74 fn from(value: QueryStatement) -> Self {
75 match value {
76 QueryStatement::Select(stmt) => Self::Select(stmt),
77 QueryStatement::Insert(stmt) => Self::Insert(stmt),
78 QueryStatement::Update(stmt) => Self::Update(stmt),
79 QueryStatement::Delete(stmt) => Self::Delete(stmt),
80 }
81 }
82}
83
84impl From<SelectStatement> for ExplainableStatement {
85 fn from(value: SelectStatement) -> Self {
86 Self::Select(value)
87 }
88}
89
90impl From<InsertStatement> for ExplainableStatement {
91 fn from(value: InsertStatement) -> Self {
92 Self::Insert(value)
93 }
94}
95
96impl From<UpdateStatement> for ExplainableStatement {
97 fn from(value: UpdateStatement) -> Self {
98 Self::Update(value)
99 }
100}
101
102impl From<DeleteStatement> for ExplainableStatement {
103 fn from(value: DeleteStatement) -> Self {
104 Self::Delete(value)
105 }
106}
107
108#[derive(Default, Debug, Clone, PartialEq)]
109pub struct ExplainStatement {
110 pub(crate) statement: Option<ExplainableStatement>,
111 #[cfg(any(feature = "backend-postgres", feature = "backend-mysql"))]
112 pub(crate) analyze: Option<bool>,
113 #[cfg(any(feature = "backend-postgres", feature = "backend-mysql"))]
114 pub(crate) format: Option<ExplainFormat>,
115 #[cfg(feature = "backend-postgres")]
116 pub(crate) pg_opts: PgExplainOptions,
117 #[cfg(feature = "backend-mysql")]
118 pub(crate) mysql_opts: MySqlExplainOptions,
119 #[cfg(feature = "backend-sqlite")]
120 pub(crate) sqlite_opts: SqliteExplainOptions,
121}
122
123impl ExplainStatement {
124 pub fn new() -> Self {
125 Self::default()
126 }
127
128 pub fn statement(mut self, statement: impl Into<ExplainableStatement>) -> Self {
130 self.statement = Some(statement.into());
131 self
132 }
133}
134
135#[cfg(any(feature = "backend-postgres", feature = "backend-mysql"))]
136impl ExplainStatement {
137 pub fn analyze(mut self) -> Self {
139 self.analyze = Some(true);
140 self
141 }
142
143 pub fn format(mut self, format: ExplainFormat) -> Self {
145 self.format = Some(format);
146 self
147 }
148}
149
150#[cfg(feature = "backend-postgres")]
151impl ExplainStatement {
152 pub fn verbose(mut self, verbose: bool) -> Self {
154 self.pg_opts.verbose = Some(verbose);
155 self
156 }
157
158 pub fn costs(mut self, costs: bool) -> Self {
160 self.pg_opts.costs = Some(costs);
161 self
162 }
163
164 pub fn settings(mut self, settings: bool) -> Self {
166 self.pg_opts.settings = Some(settings);
167 self
168 }
169
170 pub fn generic_plan(mut self, generic_plan: bool) -> Self {
172 self.pg_opts.generic_plan = Some(generic_plan);
173 self
174 }
175
176 pub fn buffers(mut self, buffers: bool) -> Self {
178 self.pg_opts.buffers = Some(buffers);
179 self
180 }
181
182 pub fn serialize_text(mut self) -> Self {
184 self.pg_opts.serialize = Some(PgExplainSerialize::Text);
185 self
186 }
187
188 pub fn serialize_binary(mut self) -> Self {
190 self.pg_opts.serialize = Some(PgExplainSerialize::Binary);
191 self
192 }
193
194 pub fn serialize_none(mut self) -> Self {
196 self.pg_opts.serialize = Some(PgExplainSerialize::None);
197 self
198 }
199
200 pub fn wal(mut self, wal: bool) -> Self {
202 self.pg_opts.wal = Some(wal);
203 self
204 }
205
206 pub fn timing(mut self, timing: bool) -> Self {
208 self.pg_opts.timing = Some(timing);
209 self
210 }
211
212 pub fn summary(mut self, summary: bool) -> Self {
214 self.pg_opts.summary = Some(summary);
215 self
216 }
217
218 pub fn memory(mut self, memory: bool) -> Self {
220 self.pg_opts.memory = Some(memory);
221 self
222 }
223}
224
225#[cfg(feature = "backend-mysql")]
226impl ExplainStatement {
227 pub fn table(table: impl IntoTableRef) -> Self {
248 let mut statement = Self::new();
249 statement.mysql_opts.table = Some(table.into_table_ref());
250 statement.mysql_opts.target = None;
251 statement
252 }
253
254 pub fn table_with_column(table: impl IntoTableRef, column: impl IntoIden) -> Self {
255 let mut statement = Self::new();
256 statement.mysql_opts.table = Some(table.into_table_ref());
257 statement.mysql_opts.target = Some(ExplainTableTarget::Column(column.into_iden()));
258 statement
259 }
260
261 pub fn table_with_wildcard(table: impl IntoTableRef, wildcard: &'static str) -> Self {
262 let mut statement = Self::new();
263 statement.mysql_opts.table = Some(table.into_table_ref());
264 statement.mysql_opts.target = Some(ExplainTableTarget::Wildcard(wildcard));
265 statement
266 }
267
268 pub fn into_variable(mut self, variable: impl Into<String>) -> Self {
270 self.mysql_opts.into_variable = Some(variable.into());
271 self
272 }
273
274 pub fn column(mut self, column: impl IntoIden) -> Self {
276 self.mysql_opts.target = Some(ExplainTableTarget::Column(column.into_iden()));
277 self
278 }
279
280 pub fn for_connection(mut self, id: u64) -> Self {
282 self.mysql_opts.for_connection = Some(id);
283 self
284 }
285
286 pub fn for_schema(mut self, schema: impl IntoIden) -> Self {
288 self.mysql_opts.schema_spec = Some(MySqlExplainSchemaSpec::Schema(schema.into_iden()));
289 self
290 }
291
292 pub fn for_database(mut self, database: impl IntoIden) -> Self {
294 self.mysql_opts.schema_spec = Some(MySqlExplainSchemaSpec::Database(database.into_iden()));
295 self
296 }
297
298 }
300
301#[cfg(feature = "backend-sqlite")]
302impl ExplainStatement {
303 pub fn query_plan(mut self) -> Self {
305 self.sqlite_opts.query_plan = true;
306 self
307 }
308}
309
310impl ExplainStatement {
311 pub fn build_collect_into(&self, query_builder: impl QueryBuilder, sql: &mut impl SqlWriter) {
313 query_builder.prepare_explain_statement(self, sql);
314 }
315
316 pub fn build_collect(
318 &self,
319 query_builder: impl QueryBuilder,
320 sql: &mut impl SqlWriter,
321 ) -> String {
322 self.build_collect_into(query_builder, sql);
323 sql.to_string()
324 }
325
326 pub fn build(&self, query_builder: impl QueryBuilder) -> (String, Values) {
328 let (placeholder, numbered) = query_builder.placeholder();
329 let mut sql = SqlWriterValues::new(placeholder, numbered);
330 self.build_collect_into(query_builder, &mut sql);
331 sql.into_parts()
332 }
333
334 pub fn to_string(&self, query_builder: impl QueryBuilder) -> String {
336 let mut sql = String::with_capacity(256);
337 self.build_collect_into(query_builder, &mut sql);
338 sql
339 }
340}