Skip to main content

sea_query/query/
explain.rs

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    /// Set the statement to be explained.
129    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    /// Set `ANALYZE` to `true`.
138    pub fn analyze(mut self) -> Self {
139        self.analyze = Some(true);
140        self
141    }
142
143    /// Set the output format.
144    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    /// Set `VERBOSE`.
153    pub fn verbose(mut self, verbose: bool) -> Self {
154        self.pg_opts.verbose = Some(verbose);
155        self
156    }
157
158    /// Set `COSTS`.
159    pub fn costs(mut self, costs: bool) -> Self {
160        self.pg_opts.costs = Some(costs);
161        self
162    }
163
164    /// Set `SETTINGS`.
165    pub fn settings(mut self, settings: bool) -> Self {
166        self.pg_opts.settings = Some(settings);
167        self
168    }
169
170    /// Set `GENERIC_PLAN`.
171    pub fn generic_plan(mut self, generic_plan: bool) -> Self {
172        self.pg_opts.generic_plan = Some(generic_plan);
173        self
174    }
175
176    /// Set `BUFFERS`.
177    pub fn buffers(mut self, buffers: bool) -> Self {
178        self.pg_opts.buffers = Some(buffers);
179        self
180    }
181
182    /// Set `SERIALIZE TEXT`.
183    pub fn serialize_text(mut self) -> Self {
184        self.pg_opts.serialize = Some(PgExplainSerialize::Text);
185        self
186    }
187
188    /// Set `SERIALIZE BINARY`.
189    pub fn serialize_binary(mut self) -> Self {
190        self.pg_opts.serialize = Some(PgExplainSerialize::Binary);
191        self
192    }
193
194    /// Set `SERIALIZE NONE`.
195    pub fn serialize_none(mut self) -> Self {
196        self.pg_opts.serialize = Some(PgExplainSerialize::None);
197        self
198    }
199
200    /// Set `WAL`.
201    pub fn wal(mut self, wal: bool) -> Self {
202        self.pg_opts.wal = Some(wal);
203        self
204    }
205
206    /// Set `TIMING`.
207    pub fn timing(mut self, timing: bool) -> Self {
208        self.pg_opts.timing = Some(timing);
209        self
210    }
211
212    /// Set `SUMMARY`.
213    pub fn summary(mut self, summary: bool) -> Self {
214        self.pg_opts.summary = Some(summary);
215        self
216    }
217
218    /// Set `MEMORY`.
219    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    /// Create a MySQL `EXPLAIN` for `tbl_name [col_name | wild]`.
228    ///
229    /// # Examples
230    ///
231    /// ```rust
232    /// use sea_query::{ExplainStatement, MysqlQueryBuilder};
233    ///
234    /// assert_eq!(
235    ///     ExplainStatement::table("glyph").to_string(MysqlQueryBuilder),
236    ///     "EXPLAIN `glyph`"
237    /// );
238    /// assert_eq!(
239    ///     ExplainStatement::table_with_column("glyph", "size").to_string(MysqlQueryBuilder),
240    ///     "EXPLAIN `glyph` `size`"
241    /// );
242    /// assert_eq!(
243    ///     ExplainStatement::table_with_wildcard("glyph", "size_%").to_string(MysqlQueryBuilder),
244    ///     "EXPLAIN `glyph` 'size_%'"
245    /// );
246    /// ```
247    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    /// Store the EXPLAIN output into a MySQL user variable.
269    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    /// Set the column name for `EXPLAIN tbl_name col_name`.
275    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    /// Explain a statement for a specific connection id.
281    pub fn for_connection(mut self, id: u64) -> Self {
282        self.mysql_opts.for_connection = Some(id);
283        self
284    }
285
286    /// Explain a statement for a specific schema.
287    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    /// Explain a statement for a specific database.
293    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    // set_table removed: prefer constructors on ExplainStatement.
299}
300
301#[cfg(feature = "backend-sqlite")]
302impl ExplainStatement {
303    /// Use `EXPLAIN QUERY PLAN`.
304    pub fn query_plan(mut self) -> Self {
305        self.sqlite_opts.query_plan = true;
306        self
307    }
308}
309
310impl ExplainStatement {
311    /// Build SQL into the provided writer.
312    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    /// Build SQL into the provided writer and return the resulting string.
317    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    /// Build SQL and collect values.
327    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    /// Build the SQL string.
335    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}