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