rorm_sql/
select.rs

1use std::fmt::Write;
2
3use crate::conditional::{BuildCondition, Condition};
4use crate::join_table::{JoinTable, JoinTableImpl};
5use crate::limit_clause::LimitClause;
6use crate::ordering::{OrderByEntry, Ordering};
7use crate::select_column::{SelectColumn, SelectColumnImpl};
8use crate::{DBImpl, Value};
9
10/**
11Trait representing a select builder.
12 */
13pub trait Select<'until_build, 'post_query> {
14    /**
15    Set a limit to the resulting rows.
16     */
17    fn limit_clause(self, limit: LimitClause) -> Self;
18
19    /**
20    Only retrieve distinct rows.
21     */
22    fn distinct(self) -> Self;
23
24    /**
25    Set a where clause to the query.
26     */
27    fn where_clause(self, where_clause: &'until_build Condition<'post_query>) -> Self;
28
29    /**
30    Build the select query
31     */
32    fn build(self) -> (String, Vec<Value<'post_query>>);
33}
34
35/**
36Representation of the data of a SELECT operation in SQL.
37 */
38#[derive(Debug)]
39pub struct SelectData<'until_build, 'post_query> {
40    pub(crate) resulting_columns: &'until_build [SelectColumnImpl<'until_build>],
41    pub(crate) limit: Option<u64>,
42    pub(crate) offset: Option<u64>,
43    pub(crate) from_clause: &'until_build str,
44    pub(crate) where_clause: Option<&'until_build Condition<'post_query>>,
45    pub(crate) distinct: bool,
46    pub(crate) lookup: Vec<Value<'post_query>>,
47    pub(crate) join_tables: &'until_build [JoinTableImpl<'until_build, 'post_query>],
48    pub(crate) order_by_clause: &'until_build [OrderByEntry<'until_build>],
49}
50
51/**
52Implementation of the [Select] trait for the different implementations.
53
54Should only be constructed via [DBImpl::select]
55 */
56#[derive(Debug)]
57pub enum SelectImpl<'until_build, 'post_query> {
58    /**
59    SQLite representation of the SELECT operation.
60     */
61    #[cfg(feature = "sqlite")]
62    SQLite(SelectData<'until_build, 'post_query>),
63    /**
64    MySQL representation of the SELECT operation.
65     */
66    #[cfg(feature = "mysql")]
67    MySQL(SelectData<'until_build, 'post_query>),
68    /**
69    Postgres representation of the SELECT operation.
70     */
71    #[cfg(feature = "postgres")]
72    Postgres(SelectData<'until_build, 'post_query>),
73}
74
75impl<'until_build, 'post_build> Select<'until_build, 'post_build>
76    for SelectImpl<'until_build, 'post_build>
77{
78    fn limit_clause(mut self, limit: LimitClause) -> Self {
79        match self {
80            #[cfg(feature = "sqlite")]
81            SelectImpl::SQLite(ref mut d) => {
82                d.limit = Some(limit.limit);
83                d.offset = limit.offset;
84            }
85            #[cfg(feature = "mysql")]
86            SelectImpl::MySQL(ref mut d) => {
87                d.limit = Some(limit.limit);
88                d.offset = limit.offset;
89            }
90            #[cfg(feature = "postgres")]
91            SelectImpl::Postgres(ref mut d) => {
92                d.limit = Some(limit.limit);
93                d.offset = limit.offset;
94            }
95        };
96        self
97    }
98
99    fn distinct(mut self) -> Self {
100        match self {
101            #[cfg(feature = "sqlite")]
102            SelectImpl::SQLite(ref mut d) => d.distinct = true,
103            #[cfg(feature = "mysql")]
104            SelectImpl::MySQL(ref mut d) => d.distinct = true,
105            #[cfg(feature = "postgres")]
106            SelectImpl::Postgres(ref mut d) => d.distinct = true,
107        };
108        self
109    }
110
111    fn where_clause(mut self, where_clause: &'until_build Condition<'post_build>) -> Self {
112        match self {
113            #[cfg(feature = "sqlite")]
114            SelectImpl::SQLite(ref mut d) => d.where_clause = Some(where_clause),
115            #[cfg(feature = "mysql")]
116            SelectImpl::MySQL(ref mut d) => d.where_clause = Some(where_clause),
117            #[cfg(feature = "postgres")]
118            SelectImpl::Postgres(ref mut d) => d.where_clause = Some(where_clause),
119        };
120        self
121    }
122
123    fn build(self) -> (String, Vec<Value<'post_build>>) {
124        match self {
125            #[cfg(feature = "sqlite")]
126            SelectImpl::SQLite(mut d) => {
127                let mut s = format!("SELECT{} ", if d.distinct { " DISTINCT" } else { "" });
128
129                let column_len = d.resulting_columns.len();
130                for (idx, column) in d.resulting_columns.iter().enumerate() {
131                    column.build(&mut s);
132
133                    if idx != column_len - 1 {
134                        write!(s, ", ").unwrap();
135                    }
136                }
137
138                write!(s, " FROM \"{}\"", d.from_clause).unwrap();
139
140                for x in d.join_tables {
141                    write!(s, " ").unwrap();
142                    x.build(&mut s, &mut d.lookup);
143                }
144
145                if let Some(c) = d.where_clause {
146                    write!(s, " WHERE {}", c.build(DBImpl::SQLite, &mut d.lookup)).unwrap()
147                };
148
149                if !d.order_by_clause.is_empty() {
150                    write!(s, " ORDER BY ").unwrap();
151
152                    let order_by_len = d.order_by_clause.len();
153                    for (idx, entry) in d.order_by_clause.iter().enumerate() {
154                        if let Some(table_name) = entry.table_name {
155                            write!(s, "{table_name}.").unwrap();
156                        };
157                        write!(
158                            s,
159                            "{}{}",
160                            entry.column_name,
161                            match entry.ordering {
162                                Ordering::Asc => "",
163                                Ordering::Desc => " DESC",
164                            }
165                        )
166                        .unwrap();
167
168                        if idx != order_by_len - 1 {
169                            write!(s, ", ").unwrap();
170                        }
171                    }
172                };
173
174                if let Some(limit) = d.limit {
175                    write!(s, " LIMIT {limit}").unwrap();
176                    if let Some(offset) = d.offset {
177                        write!(s, " OFFSET {offset}").unwrap();
178                    }
179                };
180
181                write!(s, ";").unwrap();
182
183                (s, d.lookup)
184            }
185            #[cfg(feature = "mysql")]
186            SelectImpl::MySQL(mut d) => {
187                let mut s = format!("SELECT{} ", if d.distinct { " DISTINCT" } else { "" });
188
189                let column_len = d.resulting_columns.len();
190                for (idx, column) in d.resulting_columns.iter().enumerate() {
191                    column.build(&mut s);
192
193                    if idx != column_len - 1 {
194                        write!(s, ", ").unwrap();
195                    }
196                }
197
198                write!(s, " FROM {}", d.from_clause).unwrap();
199
200                for x in d.join_tables {
201                    write!(s, " ").unwrap();
202                    x.build(&mut s, &mut d.lookup);
203                }
204
205                if let Some(c) = d.where_clause {
206                    write!(s, " WHERE {}", c.build(DBImpl::MySQL, &mut d.lookup)).unwrap()
207                };
208
209                if !d.order_by_clause.is_empty() {
210                    write!(s, " ORDER BY ").unwrap();
211
212                    let order_by_len = d.order_by_clause.len();
213                    for (idx, entry) in d.order_by_clause.iter().enumerate() {
214                        if let Some(table_name) = entry.table_name {
215                            write!(s, "{table_name}.").unwrap();
216                        };
217                        write!(
218                            s,
219                            "{}{}",
220                            entry.column_name,
221                            match entry.ordering {
222                                Ordering::Asc => "",
223                                Ordering::Desc => " DESC",
224                            }
225                        )
226                        .unwrap();
227
228                        if idx != order_by_len - 1 {
229                            write!(s, ", ").unwrap();
230                        }
231                    }
232                };
233
234                if let Some(limit) = d.limit {
235                    write!(s, " LIMIT {limit}").unwrap();
236                    if let Some(offset) = d.offset {
237                        write!(s, " OFFSET {offset}").unwrap();
238                    }
239                };
240
241                write!(s, ";").unwrap();
242
243                (s, d.lookup)
244            }
245            #[cfg(feature = "postgres")]
246            SelectImpl::Postgres(mut d) => {
247                let mut s = format!("SELECT{} ", if d.distinct { " DISTINCT" } else { "" });
248
249                let column_len = d.resulting_columns.len();
250                for (idx, column) in d.resulting_columns.iter().enumerate() {
251                    column.build(&mut s);
252
253                    if idx != column_len - 1 {
254                        write!(s, ", ").unwrap();
255                    }
256                }
257
258                write!(s, " FROM \"{}\"", d.from_clause).unwrap();
259
260                for x in d.join_tables {
261                    write!(s, " ").unwrap();
262                    x.build(&mut s, &mut d.lookup);
263                }
264
265                if let Some(c) = d.where_clause {
266                    write!(s, " WHERE {}", c.build(DBImpl::Postgres, &mut d.lookup)).unwrap()
267                };
268
269                if !d.order_by_clause.is_empty() {
270                    write!(s, " ORDER BY ").unwrap();
271
272                    let order_by_len = d.order_by_clause.len();
273                    for (idx, entry) in d.order_by_clause.iter().enumerate() {
274                        if let Some(table_name) = entry.table_name {
275                            write!(s, "\"{table_name}\".").unwrap();
276                        };
277                        write!(
278                            s,
279                            "\"{}\"{}",
280                            entry.column_name,
281                            match entry.ordering {
282                                Ordering::Asc => "",
283                                Ordering::Desc => " DESC",
284                            }
285                        )
286                        .unwrap();
287
288                        if idx != order_by_len - 1 {
289                            write!(s, ", ").unwrap();
290                        }
291                    }
292                };
293
294                if let Some(limit) = d.limit {
295                    write!(s, " LIMIT {limit}").unwrap();
296                    if let Some(offset) = d.offset {
297                        write!(s, " OFFSET {offset}").unwrap();
298                    }
299                };
300
301                write!(s, ";").unwrap();
302
303                (s, d.lookup)
304            }
305        }
306    }
307}