sea_query/query/
with.rs

1use crate::{
2    ColumnRef, DynIden, Expr, IdenList, IntoIden, QueryBuilder, QueryStatementBuilder,
3    QueryStatementWriter, SelectExpr, SelectStatement, SqlWriter, SubQueryStatement, TableName,
4    TableRef, Values,
5};
6use inherent::inherent;
7
8/// A table definition inside a WITH clause ([WithClause]).
9///
10/// A WITH clause can contain one or multiple common table expressions ([CommonTableExpression]).
11///
12/// These named queries can act as a "query local table" that are materialized during execution and
13/// then can be used by the query prefixed with the WITH clause.
14///
15/// A WITH clause can contain multiple of these [CommonTableExpression]. (Except in the case of
16/// recursive WITH query which can only contain one [CommonTableExpression]).
17///
18/// A [CommonTableExpression] is a name, column names and a query returning data for those columns.
19///
20/// Some databases (like sqlite) restrict the acceptable kinds of queries inside of the WITH clause
21/// common table expressions. These databases only allow [SelectStatement]s to form a common table
22/// expression.
23///
24/// Other databases like postgres allow modification queries (UPDATE, DELETE) inside of the WITH
25/// clause but they have to return a table. (They must have a RETURNING clause).
26///
27/// sea-query doesn't check this or restrict the kind of [CommonTableExpression] that you can create
28/// in rust. This means that you can put an UPDATE or DELETE queries into WITH clause and sea-query
29/// will succeed in generating that kind of sql query but the execution inside the database will
30/// fail because they are invalid.
31///
32/// It is your responsibility to ensure that the kind of WITH clause that you put together makes
33/// sense and valid for that database that you are using.
34///
35/// NOTE that for recursive WITH queries (in sql: "WITH RECURSIVE") you can only have a
36/// single [CommonTableExpression] inside of the WITH clause. That query must match certain
37/// requirements:
38///   * It is a query of UNION or UNION ALL of two queries.
39///   * The first part of the query (the left side of the UNION) must be executable first in itself.
40///     It must be non-recursive. (Cannot contain self reference)
41///   * The self reference must appear in the right hand side of the UNION.
42///   * The query can only have a single self-reference.
43///   * Recursive data-modifying statements are not supported, but you can use the results of a
44///     recursive SELECT query in a data-modifying statement. (like so: WITH RECURSIVE
45///     cte_name(a,b,c,d) AS (SELECT ... UNION SELECT ... FROM ... JOIN cte_name ON ... WHERE ...)
46///     DELETE FROM table WHERE table.a = cte_name.a)
47///
48/// It is mandatory to set the [Self::table_name] and the [Self::query].
49#[derive(Debug, Clone, Default, PartialEq)]
50pub struct CommonTableExpression {
51    pub(crate) table_name: Option<DynIden>,
52    pub(crate) cols: Vec<DynIden>,
53    pub(crate) query: Option<Box<SubQueryStatement>>,
54    pub(crate) materialized: Option<bool>,
55}
56
57impl CommonTableExpression {
58    /// Construct a new [`CommonTableExpression`]
59    pub fn new() -> CommonTableExpression {
60        Self::default()
61    }
62
63    /// Sets the CTE table name of the query.
64    pub fn table_name<T>(&mut self, table_name: T) -> &mut Self
65    where
66        T: IntoIden,
67    {
68        self.table_name = Some(table_name.into_iden());
69        self
70    }
71
72    /// Adds a named column to the CTE table definition.
73    pub fn column<C>(&mut self, col: C) -> &mut Self
74    where
75        C: IntoIden,
76    {
77        self.cols.push(col.into_iden());
78        self
79    }
80
81    /// Adds a named columns to the CTE table definition.
82    pub fn columns<T, I>(&mut self, cols: I) -> &mut Self
83    where
84        T: IntoIden,
85        I: IntoIterator<Item = T>,
86    {
87        self.cols
88            .extend(cols.into_iter().map(|col| col.into_iden()));
89        self
90    }
91
92    /// Some databases allow you to put "MATERIALIZED" or "NOT MATERIALIZED" in the CTE definition.
93    /// This will affect how during the execution of [WithQuery] the CTE in the [WithClause] will be
94    /// executed. If the database doesn't support this syntax this option specified here will be
95    /// ignored and not appear in the generated sql.
96    pub fn materialized(&mut self, materialized: bool) -> &mut Self {
97        self.materialized = Some(materialized);
98        self
99    }
100
101    /// Set the query generating the CTE content. The query's result must match the defined
102    /// columns.
103    pub fn query<Q>(&mut self, query: Q) -> &mut Self
104    where
105        Q: Into<SubQueryStatement>,
106    {
107        self.query = Some(Box::new(query.into()));
108        self
109    }
110
111    /// Create a CTE from a [SelectStatement] if the selections are named columns then this will
112    /// return a [CommonTableExpression] that has the column names set. The [Self::table_name] is
113    /// set if the [SelectStatement] from clause contains at least one table.
114    pub fn from_select(select: SelectStatement) -> Self {
115        let mut cte = Self::default();
116        cte.try_set_cols_from_selects(&select.selects);
117        if let Some(from) = select.from.first() {
118            match from {
119                TableRef::Table(_, Some(alias)) => cte.set_table_name_from_select(alias),
120                TableRef::Table(TableName(_, tbl), None) => cte.set_table_name_from_select(tbl),
121                _ => {}
122            }
123        }
124        cte.query = Some(Box::new(select.into()));
125        cte
126    }
127
128    fn set_table_name_from_select(&mut self, iden: &DynIden) {
129        self.table_name = Some(format!("cte_{iden}").into_iden())
130    }
131
132    /// Set up the columns of the CTE to match the given [SelectStatement] selected columns.
133    /// This will fail if the select contains non named columns like expressions of wildcards.
134    ///
135    /// Returns true if the column setup from the select query was successful. If the returned
136    /// value is false the columns are untouched.
137    pub fn try_set_cols_from_select(&mut self, select: &SelectStatement) -> bool {
138        self.try_set_cols_from_selects(&select.selects)
139    }
140
141    fn try_set_cols_from_selects(&mut self, selects: &[SelectExpr]) -> bool {
142        let vec: Option<Vec<DynIden>> = selects
143            .iter()
144            .map(|select| {
145                if let Some(ident) = &select.alias {
146                    Some(ident.clone())
147                } else {
148                    match &select.expr {
149                        Expr::Column(ColumnRef::Column(column_name)) => {
150                            // We could depend on `itertools` instead of joining manually.
151                            let mut joined_column_name = String::new();
152                            for part in column_name.clone().into_iter() {
153                                joined_column_name.push_str(&part.0);
154                                joined_column_name.push('_');
155                            }
156                            // Remove the trailing underscore after the column name.
157                            joined_column_name.pop();
158                            Some(joined_column_name.into_iden())
159                        }
160                        _ => None,
161                    }
162                }
163            })
164            .collect();
165
166        if let Some(c) = vec {
167            self.cols = c;
168            return true;
169        }
170
171        false
172    }
173}
174
175/// For recursive [WithQuery] [WithClause]s the traversing order can be specified in some databases
176/// that support this functionality.
177#[derive(Debug, Clone, PartialEq)]
178#[non_exhaustive]
179pub enum SearchOrder {
180    /// Breadth first traversal during the execution of the recursive query.
181    BREADTH,
182    /// Depth first traversal during the execution of the recursive query.
183    DEPTH,
184}
185
186/// For recursive [WithQuery] [WithClause]s the traversing order can be specified in some databases
187/// that support this functionality.
188///
189/// The clause contains the type of traversal: [SearchOrder] and the expression that is used to
190/// construct the current path.
191///
192/// A query can have both SEARCH and CYCLE clauses.
193///
194/// Setting [Self::order] and [Self::expr] is mandatory. The [SelectExpr] used must specify an alias
195/// which will be the name that you can use to order the result of the [CommonTableExpression].
196#[derive(Debug, Clone, Default, PartialEq)]
197pub struct Search {
198    pub(crate) order: Option<SearchOrder>,
199    pub(crate) expr: Option<SelectExpr>,
200}
201
202impl Search {
203    /// Create a complete [Search] specification from the [SearchOrder] and a [SelectExpr]. The
204    /// given [SelectExpr] must have an alias specified.
205    pub fn new_from_order_and_expr<EXPR>(order: SearchOrder, expr: EXPR) -> Self
206    where
207        EXPR: Into<SelectExpr>,
208    {
209        let expr = expr.into();
210        expr.alias.as_ref().unwrap();
211        Self {
212            order: Some(order),
213            expr: Some(expr),
214        }
215    }
216
217    /// Constructs a new empty [Search].
218    pub fn new() -> Self {
219        Self::default()
220    }
221
222    /// The traversal order to be used.
223    pub fn order(&mut self, order: SearchOrder) -> &mut Self {
224        self.order = Some(order);
225        self
226    }
227
228    /// The given [SelectExpr] must have an alias specified.
229    ///
230    /// The actual expression will be the one used to track the path in the graph.
231    ///
232    /// The alias of the given [SelectExpr] will be the name of the order column generated by this
233    /// clause.
234    pub fn expr<EXPR>(&mut self, expr: EXPR) -> &mut Self
235    where
236        EXPR: Into<SelectExpr>,
237    {
238        let expr = expr.into();
239        expr.alias.as_ref().unwrap();
240        self.expr = Some(expr);
241        self
242    }
243}
244
245/// For recursive [WithQuery] [WithClauses](WithClause) the CYCLE sql clause can be specified to avoid creating
246/// an infinite traversals that loops on graph cycles indefinitely.
247///
248/// You specify an expression that identifies a node in the graph, which is used during the query execution iteration, to determine newly appended values are distinct new nodes or are already visited, and therefore they should be added into the result again.
249///
250/// A query can have both SEARCH and CYCLE clauses.
251///
252/// Setting [Self::set], [Self::expr] and [Self::using] is mandatory.
253#[derive(Debug, Clone, Default, PartialEq)]
254pub struct Cycle {
255    pub(crate) expr: Option<Expr>,
256    pub(crate) set_as: Option<DynIden>,
257    pub(crate) using: Option<DynIden>,
258}
259
260impl Cycle {
261    /// Create a complete [Search] specification from the [SearchOrder] and a [SelectExpr]. The
262    /// given [SelectExpr] must have an alias specified.
263    pub fn new_from_expr_set_using<EXPR, ID1, ID2>(expr: EXPR, set: ID1, using: ID2) -> Self
264    where
265        EXPR: Into<Expr>,
266        ID1: IntoIden,
267        ID2: IntoIden,
268    {
269        Self {
270            expr: Some(expr.into()),
271            set_as: Some(set.into_iden()),
272            using: Some(using.into_iden()),
273        }
274    }
275
276    /// Constructs a new empty [Cycle].
277    pub fn new() -> Self {
278        Self::default()
279    }
280
281    /// The expression identifying nodes.
282    pub fn expr<EXPR>(&mut self, expr: EXPR) -> &mut Self
283    where
284        EXPR: Into<Expr>,
285    {
286        self.expr = Some(expr.into());
287        self
288    }
289
290    /// The name of the boolean column containing whether we have completed a cycle or not yet
291    /// generated by this clause.
292    pub fn set<ID>(&mut self, set: ID) -> &mut Self
293    where
294        ID: IntoIden,
295    {
296        self.set_as = Some(set.into_iden());
297        self
298    }
299
300    /// The name of the array typed column that contains the node ids (generated using the
301    /// [Self::expr]) that specify the current nodes path that will be generated by this clause.
302    pub fn using<ID>(&mut self, using: ID) -> &mut Self
303    where
304        ID: IntoIden,
305    {
306        self.using = Some(using.into_iden());
307        self
308    }
309}
310
311/// A WITH clause can contain one or multiple common table expressions ([CommonTableExpression]).
312///
313/// You can use this to generate [WithQuery] by calling [WithClause::query].
314///
315/// These named queries can act as a "query local table" that are materialized during execution and
316/// then can be used by the query prefixed with the WITH clause.
317///
318/// A WITH clause can contain multiple of these [CommonTableExpression]. (Except in the case of
319/// recursive WITH query which can only contain one [CommonTableExpression]).
320///
321/// A [CommonTableExpression] is a name, column names and a query returning data for those columns.
322///
323/// Some databases (like sqlite) restrict the acceptable kinds of queries inside of the WITH clause
324/// common table expressions. These databases only allow [SelectStatement]s to form a common table
325/// expression.
326///
327/// Other databases like postgres allow modification queries (UPDATE, DELETE) inside of the WITH
328/// clause but they have to return a table. (They must have a RETURNING clause).
329///
330/// sea-query doesn't check this or restrict the kind of [CommonTableExpression] that you can create
331/// in rust. This means that you can put an UPDATE or DELETE queries into WITH clause and sea-query
332/// will succeed in generating that kind of sql query but the execution inside the database will
333/// fail because they are invalid.
334///
335/// It is your responsibility to ensure that the kind of WITH clause that you put together makes
336/// sense and valid for that database that you are using.
337///
338/// NOTE that for recursive WITH queries (in sql: "WITH RECURSIVE") you can only have a
339/// single [CommonTableExpression] inside of the WITH clause. That query must match certain
340/// requirements:
341///   * It is a query of UNION or UNION ALL of two queries.
342///   * The first part of the query (the left side of the UNION) must be executable first in itself.
343///     It must be non-recursive. (Cannot contain self reference)
344///   * The self reference must appear in the right hand side of the UNION.
345///   * The query can only have a single self-reference.
346///   * Recursive data-modifying statements are not supported, but you can use the results of a
347///     recursive SELECT query in a data-modifying statement. (like so: WITH RECURSIVE
348///     cte_name(a,b,c,d) AS (SELECT ... UNION SELECT ... FROM ... JOIN cte_name ON ... WHERE ...)
349///     DELETE FROM table WHERE table.a = cte_name.a)
350///
351/// It is mandatory to set the [Self::cte]. With queries must have at least one CTE.
352/// Recursive with query generation will panic if you specify more than one CTE.
353///
354/// # Examples
355///
356/// ```
357/// use sea_query::{*, IntoCondition, IntoIden, tests_cfg::*};
358///
359/// let base_query = SelectStatement::new()
360///                     .column("id")
361///                     .expr(1i32)
362///                     .column("next")
363///                     .column("value")
364///                     .from("table")
365///                     .to_owned();
366///
367/// let cte_referencing = SelectStatement::new()
368///                             .column("id")
369///                             .expr(Expr::col("depth").add(1i32))
370///                             .column("next")
371///                             .column("value")
372///                             .from("table")
373///                             .join(
374///                                 JoinType::InnerJoin,
375///                                 "cte_traversal",
376///                                 Expr::col(("cte_traversal", "next")).equals(("table", "id"))
377///                             )
378///                             .to_owned();
379///
380/// let common_table_expression = CommonTableExpression::new()
381///             .query(
382///                 base_query.clone().union(UnionType::All, cte_referencing).to_owned()
383///             )
384///             .column("id")
385///             .column("depth")
386///             .column("next")
387///             .column("value")
388///             .table_name("cte_traversal")
389///             .to_owned();
390///
391/// let select = SelectStatement::new()
392///         .column(Asterisk)
393///         .from("cte_traversal")
394///         .to_owned();
395///
396/// let with_clause = WithClause::new()
397///         .recursive(true)
398///         .cte(common_table_expression)
399///         .cycle(Cycle::new_from_expr_set_using(Expr::Column("id".into_column_ref()), "looped", "traversal_path"))
400///         .to_owned();
401///
402/// let query = select.with(with_clause).to_owned();
403///
404/// assert_eq!(
405///     query.to_string(MysqlQueryBuilder),
406///     r#"WITH RECURSIVE `cte_traversal` (`id`, `depth`, `next`, `value`) AS (SELECT `id`, 1, `next`, `value` FROM `table` UNION ALL (SELECT `id`, `depth` + 1, `next`, `value` FROM `table` INNER JOIN `cte_traversal` ON `cte_traversal`.`next` = `table`.`id`)) SELECT * FROM `cte_traversal`"#
407/// );
408/// assert_eq!(
409///     query.to_string(PostgresQueryBuilder),
410///     r#"WITH RECURSIVE "cte_traversal" ("id", "depth", "next", "value") AS (SELECT "id", 1, "next", "value" FROM "table" UNION ALL (SELECT "id", "depth" + 1, "next", "value" FROM "table" INNER JOIN "cte_traversal" ON "cte_traversal"."next" = "table"."id")) CYCLE "id" SET "looped" USING "traversal_path" SELECT * FROM "cte_traversal""#
411/// );
412/// assert_eq!(
413///     query.to_string(SqliteQueryBuilder),
414///     r#"WITH RECURSIVE "cte_traversal" ("id", "depth", "next", "value") AS (SELECT "id", 1, "next", "value" FROM "table" UNION ALL SELECT "id", "depth" + 1, "next", "value" FROM "table" INNER JOIN "cte_traversal" ON "cte_traversal"."next" = "table"."id") SELECT * FROM "cte_traversal""#
415/// );
416/// ```
417#[derive(Debug, Clone, Default, PartialEq)]
418pub struct WithClause {
419    pub(crate) recursive: bool,
420    pub(crate) search: Option<Search>,
421    pub(crate) cycle: Option<Cycle>,
422    pub(crate) cte_expressions: Vec<CommonTableExpression>,
423}
424
425impl WithClause {
426    /// Constructs a new [WithClause].
427    pub fn new() -> Self {
428        Self::default()
429    }
430
431    /// Sets whether this clause is a recursive with clause of not.
432    /// If set to true it will generate a 'WITH RECURSIVE' query.
433    ///
434    /// You can only specify a single [CommonTableExpression] containing a union query
435    /// if this is set to true.
436    pub fn recursive(&mut self, recursive: bool) -> &mut Self {
437        self.recursive = recursive;
438        self
439    }
440
441    /// For recursive WITH queries you can specify the [Search] clause.
442    ///
443    /// This setting is not meaningful if the query is not recursive.
444    ///
445    /// Some databases don't support this clause. In that case this option will be silently ignored.
446    pub fn search(&mut self, search: Search) -> &mut Self {
447        self.search = Some(search);
448        self
449    }
450
451    /// For recursive WITH queries you can specify the [Cycle] clause.
452    ///
453    /// This setting is not meaningful if the query is not recursive.
454    ///
455    /// Some databases don't support this clause. In that case this option will be silently ignored.
456    pub fn cycle(&mut self, cycle: Cycle) -> &mut Self {
457        self.cycle = Some(cycle);
458        self
459    }
460
461    /// Add a [CommonTableExpression] to this with clause.
462    pub fn cte(&mut self, cte: CommonTableExpression) -> &mut Self {
463        self.cte_expressions.push(cte);
464        self
465    }
466
467    /// You can turn this into a [WithQuery] using this function. The resulting WITH query will
468    /// execute the argument query with this WITH clause.
469    pub fn query<T>(self, query: T) -> WithQuery
470    where
471        T: Into<SubQueryStatement>,
472    {
473        WithQuery::new().with_clause(self).query(query).to_owned()
474    }
475}
476
477impl From<CommonTableExpression> for WithClause {
478    fn from(cte: CommonTableExpression) -> WithClause {
479        WithClause::new().cte(cte).to_owned()
480    }
481}
482
483/// A WITH query. A simple SQL query that has a WITH clause ([WithClause]).
484///
485/// The [WithClause] can contain one or multiple common table expressions ([CommonTableExpression]).
486///
487/// These named queries can act as a "query local table" that are materialized during execution and
488/// then can be used by the query prefixed with the WITH clause.
489///
490/// A WITH clause can contain multiple of these [CommonTableExpression]. (Except in the case of
491/// recursive WITH query which can only contain one [CommonTableExpression]).
492///
493/// A [CommonTableExpression] is a name, column names and a query returning data for those columns.
494///
495/// Some databases (like sqlite) restrict the acceptable kinds of queries inside of the WITH clause
496/// common table expressions. These databases only allow [SelectStatement]s to form a common table
497/// expression.
498///
499/// Other databases like postgres allow modification queries (UPDATE, DELETE) inside of the WITH
500/// clause but they have to return a table. (They must have a RETURNING clause).
501///
502/// sea-query doesn't check this or restrict the kind of [CommonTableExpression] that you can create
503/// in rust. This means that you can put an UPDATE or DELETE queries into WITH clause and sea-query
504/// will succeed in generating that kind of sql query but the execution inside the database will
505/// fail because they are invalid.
506///
507/// It is your responsibility to ensure that the kind of WITH clause that you put together makes
508/// sense and valid for that database that you are using.
509///
510/// NOTE that for recursive WITH queries (in sql: "WITH RECURSIVE") you can only have a
511/// single [CommonTableExpression] inside of the WITH clause. That query must match certain
512/// requirements:
513///   * It is a query of UNION or UNION ALL of two queries.
514///   * The first part of the query (the left side of the UNION) must be executable first in itself.
515///     It must be non-recursive. (Cannot contain self reference)
516///   * The self reference must appear in the right hand side of the UNION.
517///   * The query can only have a single self-reference.
518///   * Recursive data-modifying statements are not supported, but you can use the results of a
519///     recursive SELECT query in a data-modifying statement. (like so: WITH RECURSIVE
520///     cte_name(a,b,c,d) AS (SELECT ... UNION SELECT ... FROM ... JOIN cte_name ON ... WHERE ...)
521///     DELETE FROM table WHERE table.a = cte_name.a)
522///
523/// It is mandatory to set the [Self::cte] and the [Self::query].
524#[derive(Debug, Clone, Default, PartialEq)]
525pub struct WithQuery {
526    pub(crate) with_clause: WithClause,
527    pub(crate) query: Option<Box<SubQueryStatement>>,
528}
529
530impl WithQuery {
531    /// Constructs a new empty [WithQuery].
532    pub fn new() -> Self {
533        Self::default()
534    }
535
536    /// Set the whole [WithClause].
537    pub fn with_clause(&mut self, with_clause: WithClause) -> &mut Self {
538        self.with_clause = with_clause;
539        self
540    }
541
542    /// Set the [WithClause::recursive]. See that method for more information.
543    pub fn recursive(&mut self, recursive: bool) -> &mut Self {
544        self.with_clause.recursive = recursive;
545        self
546    }
547
548    /// Add the [WithClause::search]. See that method for more information.
549    pub fn search(&mut self, search: Search) -> &mut Self {
550        self.with_clause.search = Some(search);
551        self
552    }
553
554    /// Set the [WithClause::cycle]. See that method for more information.
555    pub fn cycle(&mut self, cycle: Cycle) -> &mut Self {
556        self.with_clause.cycle = Some(cycle);
557        self
558    }
559
560    /// Add a [CommonTableExpression] to the with clause. See [WithClause::cte].
561    pub fn cte(&mut self, cte: CommonTableExpression) -> &mut Self {
562        self.with_clause.cte_expressions.push(cte);
563        self
564    }
565
566    /// Set the query that you execute with the [WithClause].
567    pub fn query<T>(&mut self, query: T) -> &mut Self
568    where
569        T: Into<SubQueryStatement>,
570    {
571        self.query = Some(Box::new(query.into()));
572        self
573    }
574}
575
576impl QueryStatementBuilder for WithQuery {
577    fn build_collect_any_into(&self, query_builder: &dyn QueryBuilder, sql: &mut dyn SqlWriter) {
578        query_builder.prepare_with_query(self, sql);
579    }
580}
581
582impl From<WithQuery> for SubQueryStatement {
583    fn from(s: WithQuery) -> Self {
584        Self::WithStatement(s)
585    }
586}
587
588#[inherent]
589impl QueryStatementWriter for WithQuery {
590    pub fn build_collect_into<T: QueryBuilder>(&self, query_builder: T, sql: &mut dyn SqlWriter) {
591        query_builder.prepare_with_query(self, sql);
592    }
593
594    pub fn build_collect<T: QueryBuilder>(
595        &self,
596        query_builder: T,
597        sql: &mut dyn SqlWriter,
598    ) -> String;
599    pub fn build<T: QueryBuilder>(&self, query_builder: T) -> (String, Values);
600    pub fn to_string<T: QueryBuilder>(&self, query_builder: T) -> String;
601}