grafbase_sql_ast/ast/
table.rs

1use super::{ExpressionKind, Join, JoinData};
2use crate::ast::{Expression, Select, Values};
3use std::borrow::Cow;
4
5/// An object that can be aliased.
6pub trait Aliasable<'a> {
7    type Target;
8
9    /// Alias table for usage elsewhere in the query.
10    fn alias<T>(self, alias: T) -> Self::Target
11    where
12        T: Into<Cow<'a, str>>;
13}
14
15#[derive(Clone, Debug, PartialEq)]
16/// Either an identifier or a nested query.
17pub enum TableType<'a> {
18    Table(Cow<'a, str>),
19    JoinedTable(Box<(Cow<'a, str>, Vec<Join<'a>>)>),
20    Query(Box<Select<'a>>),
21    Values(Values<'a>),
22}
23
24/// A table definition
25#[derive(Clone, Debug)]
26pub struct Table<'a> {
27    pub typ: TableType<'a>,
28    pub alias: Option<Cow<'a, str>>,
29    pub database: Option<Cow<'a, str>>,
30}
31
32impl<'a> PartialEq for Table<'a> {
33    fn eq(&self, other: &Table) -> bool {
34        self.typ == other.typ && self.database == other.database
35    }
36}
37
38impl<'a> Table<'a> {
39    /// Define in which database the table is located
40    pub fn database<T>(mut self, database: T) -> Self
41    where
42        T: Into<Cow<'a, str>>,
43    {
44        self.database = Some(database.into());
45        self
46    }
47
48    /// A qualified asterisk to this table
49    pub fn asterisk(self) -> Expression<'a> {
50        Expression {
51            kind: ExpressionKind::Asterisk(Some(Box::new(self))),
52            alias: None,
53        }
54    }
55
56    /// Adds a `LEFT JOIN` clause to the query, specifically for that table.
57    /// Useful to positionally add a JOIN clause in case you are selecting from multiple tables.
58    pub fn left_join<J>(mut self, join: J) -> Self
59    where
60        J: Into<JoinData<'a>>,
61    {
62        match self.typ {
63            TableType::Table(table_name) => {
64                self.typ =
65                    TableType::JoinedTable(Box::new((table_name, vec![Join::Left(join.into())])))
66            }
67            TableType::JoinedTable(ref mut jt) => jt.1.push(Join::Left(join.into())),
68            TableType::Query(_) => {
69                panic!("You cannot left_join on a table of type Query")
70            }
71            TableType::Values(_) => {
72                panic!("You cannot left_join on a table of type Values")
73            }
74        }
75
76        self
77    }
78
79    /// Adds an `INNER JOIN` clause to the query, specifically for that table.
80    /// Useful to positionally add a JOIN clause in case you are selecting from multiple tables.
81    pub fn inner_join<J>(mut self, join: J) -> Self
82    where
83        J: Into<JoinData<'a>>,
84    {
85        match self.typ {
86            TableType::Table(table_name) => {
87                self.typ =
88                    TableType::JoinedTable(Box::new((table_name, vec![Join::Inner(join.into())])))
89            }
90            TableType::JoinedTable(ref mut jt) => jt.1.push(Join::Inner(join.into())),
91            TableType::Query(_) => {
92                panic!("You cannot inner_join on a table of type Query")
93            }
94            TableType::Values(_) => {
95                panic!("You cannot inner_join on a table of type Values")
96            }
97        }
98
99        self
100    }
101
102    /// Adds a `RIGHT JOIN` clause to the query, specifically for that table.
103    /// Useful to positionally add a JOIN clause in case you are selecting from multiple tables.
104    pub fn right_join<J>(mut self, join: J) -> Self
105    where
106        J: Into<JoinData<'a>>,
107    {
108        match self.typ {
109            TableType::Table(table_name) => {
110                self.typ =
111                    TableType::JoinedTable(Box::new((table_name, vec![Join::Right(join.into())])))
112            }
113            TableType::JoinedTable(ref mut jt) => jt.1.push(Join::Right(join.into())),
114            TableType::Query(_) => {
115                panic!("You cannot right_join on a table of type Query")
116            }
117            TableType::Values(_) => {
118                panic!("You cannot right_join on a table of type Values")
119            }
120        }
121
122        self
123    }
124
125    /// Adds a `FULL JOIN` clause to the query, specifically for that table.
126    /// Useful to positionally add a JOIN clause in case you are selecting from multiple tables.
127    pub fn full_join<J>(mut self, join: J) -> Self
128    where
129        J: Into<JoinData<'a>>,
130    {
131        match self.typ {
132            TableType::Table(table_name) => {
133                self.typ =
134                    TableType::JoinedTable(Box::new((table_name, vec![Join::Full(join.into())])))
135            }
136            TableType::JoinedTable(ref mut jt) => jt.1.push(Join::Full(join.into())),
137            TableType::Query(_) => {
138                panic!("You cannot full_join on a table of type Query")
139            }
140            TableType::Values(_) => {
141                panic!("You cannot full_join on a table of type Values")
142            }
143        }
144
145        self
146    }
147}
148
149impl<'a> From<&'a str> for Table<'a> {
150    fn from(s: &'a str) -> Table<'a> {
151        Table {
152            typ: TableType::Table(s.into()),
153            alias: None,
154            database: None,
155        }
156    }
157}
158
159impl<'a> From<&'a String> for Table<'a> {
160    fn from(s: &'a String) -> Table<'a> {
161        Table {
162            typ: TableType::Table(s.into()),
163            alias: None,
164            database: None,
165        }
166    }
167}
168
169impl<'a> From<(&'a str, &'a str)> for Table<'a> {
170    fn from(s: (&'a str, &'a str)) -> Table<'a> {
171        let table: Table<'a> = s.1.into();
172        table.database(s.0)
173    }
174}
175
176impl<'a> From<(&'a str, &'a String)> for Table<'a> {
177    fn from(s: (&'a str, &'a String)) -> Table<'a> {
178        let table: Table<'a> = s.1.into();
179        table.database(s.0)
180    }
181}
182
183impl<'a> From<(&'a String, &'a str)> for Table<'a> {
184    fn from(s: (&'a String, &'a str)) -> Table<'a> {
185        let table: Table<'a> = s.1.into();
186        table.database(s.0)
187    }
188}
189
190impl<'a> From<(&'a String, &'a String)> for Table<'a> {
191    fn from(s: (&'a String, &'a String)) -> Table<'a> {
192        let table: Table<'a> = s.1.into();
193        table.database(s.0)
194    }
195}
196
197impl<'a> From<String> for Table<'a> {
198    fn from(s: String) -> Self {
199        Table {
200            typ: TableType::Table(s.into()),
201            alias: None,
202            database: None,
203        }
204    }
205}
206
207impl<'a> From<(String, String)> for Table<'a> {
208    fn from(s: (String, String)) -> Table<'a> {
209        let table: Table<'a> = s.1.into();
210        table.database(s.0)
211    }
212}
213
214impl<'a> From<Select<'a>> for Table<'a> {
215    fn from(select: Select<'a>) -> Self {
216        Table {
217            typ: TableType::Query(Box::new(select)),
218            alias: None,
219            database: None,
220        }
221    }
222}
223
224impl<'a> Aliasable<'a> for Table<'a> {
225    type Target = Table<'a>;
226
227    fn alias<T>(mut self, alias: T) -> Self::Target
228    where
229        T: Into<Cow<'a, str>>,
230    {
231        self.alias = Some(alias.into());
232        self
233    }
234}