Skip to main content

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