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}