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#[derive(Debug)]
14pub struct Select<'until_build, 'post_query> {
15 pub(crate) db_impl: DBImpl,
17 pub(crate) resulting_columns: &'until_build [SelectColumnImpl<'until_build>],
18 pub(crate) from_clause: &'until_build str,
19 pub(crate) join_tables: &'until_build [JoinTableImpl<'until_build, 'post_query>],
20 pub(crate) order_by_clause: &'until_build [OrderByEntry<'until_build>],
21
22 pub(crate) where_clause: Option<&'until_build Condition<'post_query>>,
24 pub(crate) distinct: bool,
25 pub(crate) limit: Option<u64>,
26 pub(crate) offset: Option<u64>,
27
28 #[cfg(feature = "postgres-only")]
29 pub(crate) locking_clause: Option<LockingClause>,
30}
31
32#[cfg(feature = "postgres-only")]
34#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
35pub struct LockingClause {
36 pub strength: LockStrength,
38
39 pub acquire: LockAcquire,
41}
42
43#[cfg(feature = "postgres-only")]
44#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
46pub enum LockStrength {
47 Update,
49 NoKeyUpdate,
51 Share,
53 KeyShare,
55}
56
57#[cfg(feature = "postgres-only")]
58#[derive(Default, Copy, Clone, Debug, Eq, PartialEq, Hash)]
60pub enum LockAcquire {
61 #[default]
63 Wait,
64 NoWait,
66 SkipLocked,
68}
69
70impl<'until_build, 'post_build> Select<'until_build, 'post_build> {
71 pub fn limit_clause(mut self, limit: LimitClause) -> Self {
73 self.limit = Some(limit.limit);
74 self.offset = limit.offset;
75 self
76 }
77
78 pub fn distinct(mut self) -> Self {
80 self.distinct = true;
81 self
82 }
83
84 pub fn where_clause(mut self, where_clause: &'until_build Condition<'post_build>) -> Self {
86 self.where_clause = Some(where_clause);
87 self
88 }
89
90 #[cfg(feature = "postgres-only")]
92 pub fn locking_clause(mut self, locking: LockingClause) -> Self {
93 self.locking_clause = Some(locking);
94 self
95 }
96
97 pub fn build(self) -> (String, Vec<Value<'post_build>>) {
99 let mut sql;
100 let mut values = Vec::new();
101
102 match self.db_impl {
103 #[cfg(feature = "sqlite")]
104 DBImpl::SQLite => {
105 sql = format!("SELECT{} ", if self.distinct { " DISTINCT" } else { "" });
106
107 let column_len = self.resulting_columns.len();
108 for (idx, column) in self.resulting_columns.iter().enumerate() {
109 column.build(&mut sql);
110
111 if idx != column_len - 1 {
112 write!(sql, ", ").unwrap();
113 }
114 }
115
116 write!(sql, " FROM \"{}\"", self.from_clause).unwrap();
117
118 for x in self.join_tables {
119 write!(sql, " ").unwrap();
120 x.build(&mut sql, &mut values);
121 }
122
123 if let Some(c) = self.where_clause {
124 write!(sql, " WHERE {}", c.build(DBImpl::SQLite, &mut values)).unwrap()
125 };
126
127 if !self.order_by_clause.is_empty() {
128 write!(sql, " ORDER BY ").unwrap();
129
130 let order_by_len = self.order_by_clause.len();
131 for (idx, entry) in self.order_by_clause.iter().enumerate() {
132 if let Some(table_name) = entry.table_name {
133 write!(sql, "{table_name}.").unwrap();
134 };
135 write!(
136 sql,
137 "{}{}",
138 entry.column_name,
139 match entry.ordering {
140 Ordering::Asc => "",
141 Ordering::Desc => " DESC",
142 }
143 )
144 .unwrap();
145
146 if idx != order_by_len - 1 {
147 write!(sql, ", ").unwrap();
148 }
149 }
150 };
151
152 if let Some(limit) = self.limit {
153 write!(sql, " LIMIT {limit}").unwrap();
154 if let Some(offset) = self.offset {
155 write!(sql, " OFFSET {offset}").unwrap();
156 }
157 };
158
159 write!(sql, ";").unwrap();
160 }
161 #[cfg(feature = "postgres")]
162 DBImpl::Postgres => {
163 sql = format!("SELECT{} ", if self.distinct { " DISTINCT" } else { "" });
164
165 let column_len = self.resulting_columns.len();
166 for (idx, column) in self.resulting_columns.iter().enumerate() {
167 column.build(&mut sql);
168
169 if idx != column_len - 1 {
170 write!(sql, ", ").unwrap();
171 }
172 }
173
174 write!(sql, " FROM \"{}\"", self.from_clause).unwrap();
175
176 for x in self.join_tables {
177 write!(sql, " ").unwrap();
178 x.build(&mut sql, &mut values);
179 }
180
181 if let Some(c) = self.where_clause {
182 write!(sql, " WHERE {}", c.build(DBImpl::Postgres, &mut values)).unwrap()
183 };
184
185 if !self.order_by_clause.is_empty() {
186 write!(sql, " ORDER BY ").unwrap();
187
188 let order_by_len = self.order_by_clause.len();
189 for (idx, entry) in self.order_by_clause.iter().enumerate() {
190 if let Some(table_name) = entry.table_name {
191 write!(sql, "\"{table_name}\".").unwrap();
192 };
193 write!(
194 sql,
195 "\"{}\"{}",
196 entry.column_name,
197 match entry.ordering {
198 Ordering::Asc => "",
199 Ordering::Desc => " DESC",
200 }
201 )
202 .unwrap();
203
204 if idx != order_by_len - 1 {
205 write!(sql, ", ").unwrap();
206 }
207 }
208 };
209
210 if let Some(limit) = self.limit {
211 write!(sql, " LIMIT {limit}").unwrap();
212 if let Some(offset) = self.offset {
213 write!(sql, " OFFSET {offset}").unwrap();
214 }
215 };
216
217 #[cfg(feature = "postgres-only")]
218 if let Some(locking) = self.locking_clause {
219 write!(
220 sql,
221 " FOR {}{}",
222 match locking.strength {
223 LockStrength::Update => "UPDATE",
224 LockStrength::NoKeyUpdate => "NO KEY UPDATE",
225 LockStrength::Share => "SHARE",
226 LockStrength::KeyShare => "KEY SHARE",
227 },
228 match locking.acquire {
229 LockAcquire::Wait => "",
230 LockAcquire::NoWait => " NOWAIT",
231 LockAcquire::SkipLocked => " SKIP LOCKED",
232 }
233 )
234 .unwrap();
235 }
236
237 write!(sql, ";").unwrap();
238 }
239 }
240
241 (sql, values)
242 }
243}