quaint_forked/ast/
table.rs

1use super::{Column, Comparable, ConditionTree, DefaultValue, ExpressionKind, IndexDefinition, Join, JoinData};
2use crate::{
3    ast::{Expression, Row, Select, Values},
4    error::{Error, ErrorKind},
5};
6use std::borrow::Cow;
7
8/// An object that can be aliased.
9pub trait Aliasable<'a> {
10    type Target;
11
12    /// Alias table for usage elsewhere in the query.
13    fn alias<T>(self, alias: T) -> Self::Target
14    where
15        T: Into<Cow<'a, str>>;
16}
17
18#[derive(Clone, Debug, PartialEq)]
19/// Either an identifier or a nested query.
20pub enum TableType<'a> {
21    Table(Cow<'a, str>),
22    JoinedTable(Box<(Cow<'a, str>, Vec<Join<'a>>)>),
23    Query(Box<Select<'a>>),
24    Values(Values<'a>),
25}
26
27/// A table definition
28#[derive(Clone, Debug)]
29pub struct Table<'a> {
30    pub typ: TableType<'a>,
31    pub alias: Option<Cow<'a, str>>,
32    pub database: Option<Cow<'a, str>>,
33    pub(crate) index_definitions: Vec<IndexDefinition<'a>>,
34}
35
36impl<'a> PartialEq for Table<'a> {
37    fn eq(&self, other: &Table) -> bool {
38        self.typ == other.typ && self.database == other.database
39    }
40}
41
42impl<'a> Table<'a> {
43    /// Define in which database the table is located
44    pub fn database<T>(mut self, database: T) -> Self
45    where
46        T: Into<Cow<'a, str>>,
47    {
48        self.database = Some(database.into());
49        self
50    }
51
52    /// A qualified asterisk to this table
53    pub fn asterisk(self) -> Expression<'a> {
54        Expression {
55            kind: ExpressionKind::Asterisk(Some(Box::new(self))),
56            alias: None,
57        }
58    }
59
60    /// Add unique index definition.
61    pub fn add_unique_index(mut self, i: impl Into<IndexDefinition<'a>>) -> Self {
62        let definition = i.into();
63        self.index_definitions.push(definition.set_table(self.clone()));
64        self
65    }
66
67    /// Conditions for Microsoft T-SQL MERGE using the table metadata.
68    ///
69    /// - Find the unique indices from the table that matches the inserted columns
70    /// - Create a join from the virtual table with the uniques
71    /// - Combine joins with `OR`
72    /// - If the the index is a compound with other columns, combine them with `AND`
73    /// - If the column is not provided and index exists, try inserting a default value.
74    /// - Otherwise the function will return an error.
75    pub(crate) fn join_conditions(&self, inserted_columns: &[Column<'a>]) -> crate::Result<ConditionTree<'a>> {
76        let mut result = ConditionTree::NegativeCondition;
77
78        let join_cond = |column: &Column<'a>| {
79            let cond = if !inserted_columns.contains(column) {
80                match column.default.clone() {
81                    Some(DefaultValue::Provided(val)) => Some(column.clone().equals(val).into()),
82                    Some(DefaultValue::Generated) => None,
83                    None => {
84                        let kind =
85                            ErrorKind::conversion("A unique column missing from insert and table has no default.");
86
87                        return Err(Error::builder(kind).build());
88                    }
89                }
90            } else {
91                let dual_col = column.clone().table("dual");
92                Some(dual_col.equals(column.clone()).into())
93            };
94
95            Ok::<Option<ConditionTree>, Error>(cond)
96        };
97
98        for index in self.index_definitions.iter() {
99            match index {
100                IndexDefinition::Single(column) => {
101                    if let Some(right_cond) = join_cond(column)? {
102                        match result {
103                            ConditionTree::NegativeCondition => result = right_cond,
104                            left_cond => result = left_cond.or(right_cond),
105                        }
106                    }
107                }
108                IndexDefinition::Compound(cols) => {
109                    let mut sub_result = ConditionTree::NoCondition;
110
111                    for right in cols.iter() {
112                        let right_cond = join_cond(right)?.unwrap_or(ConditionTree::NegativeCondition);
113
114                        match sub_result {
115                            ConditionTree::NoCondition => sub_result = right_cond,
116                            left_cond => sub_result = left_cond.and(right_cond),
117                        }
118                    }
119
120                    match result {
121                        ConditionTree::NegativeCondition => result = sub_result,
122                        left_cond => result = left_cond.or(sub_result),
123                    }
124                }
125            }
126        }
127
128        Ok(result)
129    }
130
131    /// Adds a `LEFT JOIN` clause to the query, specifically for that table.
132    /// Useful to positionally add a JOIN clause in case you are selecting from multiple tables.
133    ///
134    /// ```rust
135    /// # use quaint::{ast::*, visitor::{Visitor, Sqlite}};
136    /// # fn main() -> Result<(), quaint::error::Error> {
137    /// let join = "posts".alias("p").on(("p", "visible").equals(true));
138    /// let joined_table = Table::from("users").left_join(join);
139    /// let query = Select::from_table(joined_table).and_from("comments");
140    /// let (sql, params) = Sqlite::build(query)?;
141    ///
142    /// assert_eq!(
143    ///     "SELECT `users`.*, `comments`.* FROM \
144    ///     `users` LEFT JOIN `posts` AS `p` ON `p`.`visible` = ?, \
145    ///     `comments`",
146    ///     sql
147    /// );
148    ///
149    /// assert_eq!(
150    ///     vec![
151    ///         Value::from(true),
152    ///     ],
153    ///     params
154    /// );
155    /// # Ok(())
156    /// # }
157    /// ```
158    pub fn left_join<J>(mut self, join: J) -> Self
159    where
160        J: Into<JoinData<'a>>,
161    {
162        match self.typ {
163            TableType::Table(table_name) => {
164                self.typ = TableType::JoinedTable(Box::new((table_name, vec![Join::Left(join.into())])))
165            }
166            TableType::JoinedTable(ref mut jt) => jt.1.push(Join::Left(join.into())),
167            TableType::Query(_) => {
168                panic!("You cannot left_join on a table of type Query")
169            }
170            TableType::Values(_) => {
171                panic!("You cannot left_join on a table of type Values")
172            }
173        }
174
175        self
176    }
177
178    /// Adds an `INNER JOIN` clause to the query, specifically for that table.
179    /// Useful to positionally add a JOIN clause in case you are selecting from multiple tables.
180    ///
181    /// ```rust
182    /// # use quaint::{ast::*, visitor::{Visitor, Sqlite}};
183    /// # fn main() -> Result<(), quaint::error::Error> {
184    /// let join = "posts".alias("p").on(("p", "visible").equals(true));
185    /// let joined_table = Table::from("users").inner_join(join);
186    /// let query = Select::from_table(joined_table).and_from("comments");
187    /// let (sql, params) = Sqlite::build(query)?;
188    ///
189    /// assert_eq!(
190    ///     "SELECT `users`.*, `comments`.* FROM \
191    ///     `users` INNER JOIN `posts` AS `p` ON `p`.`visible` = ?, \
192    ///     `comments`",
193    ///     sql
194    /// );
195    ///
196    /// assert_eq!(
197    ///     vec![
198    ///         Value::from(true),
199    ///     ],
200    ///     params
201    /// );
202    /// # Ok(())
203    /// # }
204    /// ```
205    pub fn inner_join<J>(mut self, join: J) -> Self
206    where
207        J: Into<JoinData<'a>>,
208    {
209        match self.typ {
210            TableType::Table(table_name) => {
211                self.typ = TableType::JoinedTable(Box::new((table_name, vec![Join::Inner(join.into())])))
212            }
213            TableType::JoinedTable(ref mut jt) => jt.1.push(Join::Inner(join.into())),
214            TableType::Query(_) => {
215                panic!("You cannot inner_join on a table of type Query")
216            }
217            TableType::Values(_) => {
218                panic!("You cannot inner_join on a table of type Values")
219            }
220        }
221
222        self
223    }
224
225    /// Adds a `RIGHT JOIN` clause to the query, specifically for that table.
226    /// Useful to positionally add a JOIN clause in case you are selecting from multiple tables.
227    ///
228    /// ```rust
229    /// # use quaint::{ast::*, visitor::{Visitor, Sqlite}};
230    /// # fn main() -> Result<(), quaint::error::Error> {
231    /// let join = "posts".alias("p").on(("p", "visible").equals(true));
232    /// let joined_table = Table::from("users").right_join(join);
233    /// let query = Select::from_table(joined_table).and_from("comments");
234    /// let (sql, params) = Sqlite::build(query)?;
235    ///
236    /// assert_eq!(
237    ///     "SELECT `users`.*, `comments`.* FROM \
238    ///     `users` RIGHT JOIN `posts` AS `p` ON `p`.`visible` = ?, \
239    ///     `comments`",
240    ///     sql
241    /// );
242    ///
243    /// assert_eq!(
244    ///     vec![
245    ///         Value::from(true),
246    ///     ],
247    ///     params
248    /// );
249    /// # Ok(())
250    /// # }
251    /// ```
252    pub fn right_join<J>(mut self, join: J) -> Self
253    where
254        J: Into<JoinData<'a>>,
255    {
256        match self.typ {
257            TableType::Table(table_name) => {
258                self.typ = TableType::JoinedTable(Box::new((table_name, vec![Join::Right(join.into())])))
259            }
260            TableType::JoinedTable(ref mut jt) => jt.1.push(Join::Right(join.into())),
261            TableType::Query(_) => {
262                panic!("You cannot right_join on a table of type Query")
263            }
264            TableType::Values(_) => {
265                panic!("You cannot right_join on a table of type Values")
266            }
267        }
268
269        self
270    }
271
272    /// Adds a `FULL JOIN` clause to the query, specifically for that table.
273    /// Useful to positionally add a JOIN clause in case you are selecting from multiple tables.
274    ///
275    /// ```rust
276    /// # use quaint::{ast::*, visitor::{Visitor, Sqlite}};
277    /// # fn main() -> Result<(), quaint::error::Error> {
278    /// let join = "posts".alias("p").on(("p", "visible").equals(true));
279    /// let joined_table = Table::from("users").full_join(join);
280    /// let query = Select::from_table(joined_table).and_from("comments");
281    /// let (sql, params) = Sqlite::build(query)?;
282    ///
283    /// assert_eq!(
284    ///     "SELECT `users`.*, `comments`.* FROM \
285    ///     `users` FULL JOIN `posts` AS `p` ON `p`.`visible` = ?, \
286    ///     `comments`",
287    ///     sql
288    /// );
289    ///
290    /// assert_eq!(
291    ///     vec![
292    ///         Value::from(true),
293    ///     ],
294    ///     params
295    /// );
296    /// # Ok(())
297    /// # }
298    /// ```
299    pub fn full_join<J>(mut self, join: J) -> Self
300    where
301        J: Into<JoinData<'a>>,
302    {
303        match self.typ {
304            TableType::Table(table_name) => {
305                self.typ = TableType::JoinedTable(Box::new((table_name, vec![Join::Full(join.into())])))
306            }
307            TableType::JoinedTable(ref mut jt) => jt.1.push(Join::Full(join.into())),
308            TableType::Query(_) => {
309                panic!("You cannot full_join on a table of type Query")
310            }
311            TableType::Values(_) => {
312                panic!("You cannot full_join on a table of type Values")
313            }
314        }
315
316        self
317    }
318}
319
320impl<'a> From<&'a str> for Table<'a> {
321    fn from(s: &'a str) -> Table<'a> {
322        Table {
323            typ: TableType::Table(s.into()),
324            alias: None,
325            database: None,
326            index_definitions: Vec::new(),
327        }
328    }
329}
330
331impl<'a> From<&'a String> for Table<'a> {
332    fn from(s: &'a String) -> Table<'a> {
333        Table {
334            typ: TableType::Table(s.into()),
335            alias: None,
336            database: None,
337            index_definitions: Vec::new(),
338        }
339    }
340}
341
342impl<'a> From<(&'a str, &'a str)> for Table<'a> {
343    fn from(s: (&'a str, &'a str)) -> Table<'a> {
344        let table: Table<'a> = s.1.into();
345        table.database(s.0)
346    }
347}
348
349impl<'a> From<(&'a str, &'a String)> for Table<'a> {
350    fn from(s: (&'a str, &'a String)) -> Table<'a> {
351        let table: Table<'a> = s.1.into();
352        table.database(s.0)
353    }
354}
355
356impl<'a> From<(&'a String, &'a str)> for Table<'a> {
357    fn from(s: (&'a String, &'a str)) -> Table<'a> {
358        let table: Table<'a> = s.1.into();
359        table.database(s.0)
360    }
361}
362
363impl<'a> From<(&'a String, &'a String)> for Table<'a> {
364    fn from(s: (&'a String, &'a String)) -> Table<'a> {
365        let table: Table<'a> = s.1.into();
366        table.database(s.0)
367    }
368}
369
370impl<'a> From<String> for Table<'a> {
371    fn from(s: String) -> Self {
372        Table {
373            typ: TableType::Table(s.into()),
374            alias: None,
375            database: None,
376            index_definitions: Vec::new(),
377        }
378    }
379}
380
381impl<'a> From<Vec<Row<'a>>> for Table<'a> {
382    fn from(values: Vec<Row<'a>>) -> Self {
383        Table::from(Values::from(values.into_iter()))
384    }
385}
386
387impl<'a> From<Values<'a>> for Table<'a> {
388    fn from(values: Values<'a>) -> Self {
389        Self {
390            typ: TableType::Values(values),
391            alias: None,
392            database: None,
393            index_definitions: Vec::new(),
394        }
395    }
396}
397
398impl<'a> From<(String, String)> for Table<'a> {
399    fn from(s: (String, String)) -> Table<'a> {
400        let table: Table<'a> = s.1.into();
401        table.database(s.0)
402    }
403}
404
405impl<'a> From<Select<'a>> for Table<'a> {
406    fn from(select: Select<'a>) -> Self {
407        Table {
408            typ: TableType::Query(Box::new(select)),
409            alias: None,
410            database: None,
411            index_definitions: Vec::new(),
412        }
413    }
414}
415
416impl<'a> Aliasable<'a> for Table<'a> {
417    type Target = Table<'a>;
418
419    fn alias<T>(mut self, alias: T) -> Self::Target
420    where
421        T: Into<Cow<'a, str>>,
422    {
423        self.alias = Some(alias.into());
424        self
425    }
426}
427
428aliasable!(String, (String, String));
429aliasable!(&'a str, (&'a str, &'a str));