quaint_forked/ast/
union.rs

1use crate::ast::{Expression, Query, Select};
2use std::{collections::BTreeSet, fmt};
3
4use super::CommonTableExpression;
5use super::IntoCommonTableExpression;
6
7#[derive(Debug, PartialEq, Clone, Copy)]
8pub(crate) enum UnionType {
9    All,
10    Distinct,
11}
12
13impl fmt::Display for UnionType {
14    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
15        match self {
16            UnionType::All => write!(f, "UNION ALL"),
17            UnionType::Distinct => write!(f, "UNION"),
18        }
19    }
20}
21
22/// A builder for a `UNION`s over multiple `SELECT` statements.
23#[derive(Debug, PartialEq, Clone, Default)]
24pub struct Union<'a> {
25    pub(crate) selects: Vec<Select<'a>>,
26    pub(crate) types: Vec<UnionType>,
27    pub(crate) ctes: Vec<CommonTableExpression<'a>>,
28}
29
30impl<'a> From<Union<'a>> for Query<'a> {
31    fn from(ua: Union<'a>) -> Self {
32        Query::Union(Box::new(ua))
33    }
34}
35
36impl<'a> From<Union<'a>> for Expression<'a> {
37    fn from(uaua: Union<'a>) -> Self {
38        Expression::union(uaua)
39    }
40}
41
42impl<'a> Union<'a> {
43    pub fn new(q: Select<'a>) -> Self {
44        Self {
45            selects: vec![q],
46            types: Vec::new(),
47            ctes: Vec::new(),
48        }
49    }
50
51    /// Creates a union with previous selection and the given `SELECT`
52    /// statement, allowing duplicates.
53    ///
54    /// ```rust
55    /// # use quaint::{ast::*, visitor::{Visitor, Sqlite}};
56    /// # fn main() -> Result<(), quaint::error::Error> {
57    /// let s1 = Select::default().value(1);
58    /// let s2 = Select::default().value(2);
59    /// let (sql, params) = Sqlite::build(Union::new(s1).all(s2))?;
60    ///
61    /// assert_eq!("SELECT ? UNION ALL SELECT ?", sql);
62    ///
63    /// assert_eq!(vec![
64    ///     Value::from(1),
65    ///     Value::from(2)
66    /// ], params);
67    /// # Ok(())
68    /// # }
69    /// ```
70    pub fn all(mut self, q: Select<'a>) -> Self {
71        self.selects.push(q);
72        self.types.push(UnionType::All);
73        self
74    }
75
76    /// Creates a union with previous selection and the given `SELECT`
77    /// statement, selecting only distinct values.
78    ///
79    /// ```rust
80    /// # use quaint::{ast::*, visitor::{Visitor, Sqlite}};
81    /// # fn main() -> Result<(), quaint::error::Error> {
82    /// let s1 = Select::default().value(1);
83    /// let s2 = Select::default().value(2);
84    /// let (sql, params) = Sqlite::build(Union::new(s1).distinct(s2))?;
85    ///
86    /// assert_eq!("SELECT ? UNION SELECT ?", sql);
87    ///
88    /// assert_eq!(vec![
89    ///     Value::from(1),
90    ///     Value::from(2)
91    /// ], params);
92    /// # Ok(())
93    /// # }
94    /// ```
95    pub fn distinct(mut self, q: Select<'a>) -> Self {
96        self.selects.push(q);
97        self.types.push(UnionType::Distinct);
98        self
99    }
100
101    /// A list of item names in the queries, skipping the anonymous values or
102    /// columns.
103    pub(crate) fn named_selection(&self) -> Vec<String> {
104        self.selects
105            .iter()
106            .fold(BTreeSet::new(), |mut acc, select| {
107                for name in select.named_selection() {
108                    acc.insert(name);
109                }
110
111                acc
112            })
113            .into_iter()
114            .collect()
115    }
116
117    /// Finds all comparisons between tuples and selects in the queries and
118    /// converts them to common table expressions for making the query
119    /// compatible with databases not supporting tuples.
120    #[cfg(feature = "mssql")]
121    pub(crate) fn convert_tuple_selects_into_ctes(
122        mut self,
123        top_level: bool,
124        level: &mut usize,
125    ) -> either::Either<Self, (Self, Vec<CommonTableExpression<'a>>)> {
126        let mut queries = Vec::with_capacity(self.selects.len());
127        let mut combined_ctes = Vec::new();
128
129        for select in self.selects.drain(0..) {
130            let (select, ctes) = select
131                .convert_tuple_selects_to_ctes(false, level)
132                .expect_right("Nested select should always be right.");
133
134            queries.push(select);
135            combined_ctes.extend(ctes);
136        }
137
138        self.selects = queries;
139
140        if top_level {
141            self.ctes = combined_ctes;
142            either::Either::Left(self)
143        } else {
144            either::Either::Right((self, combined_ctes))
145        }
146    }
147}
148
149impl<'a> IntoCommonTableExpression<'a> for Union<'a> {}