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}