1use crate::query::{Cte, CteQuery};
2use crate::util::SqlExtension;
3use crate::{Dialect, ToSql};
4
5mod expr;
6mod join;
7
8pub use expr::*;
9pub use join::*;
10
11#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct Select {
14 pub ctes: Vec<Cte>,
15 pub distinct: bool,
16 pub columns: Vec<SelectColumn>,
17 pub from: Option<From>,
18 pub join: Vec<Join>,
19 pub where_: Where,
20 pub group: Vec<GroupBy>,
21 pub having: Where,
22 pub order: Vec<OrderBy>,
23 pub limit: Option<usize>,
24 pub offset: Option<usize>,
25}
26
27impl Default for Select {
28 fn default() -> Self {
29 Self {
30 ctes: vec![],
31 distinct: false,
32 columns: vec![],
33 from: None,
34 join: vec![],
35 where_: Where::And(vec![]),
36 group: vec![],
37 having: Where::And(vec![]),
38 order: vec![],
39 limit: None,
40 offset: None,
41 }
42 }
43}
44
45impl Select {
46 pub fn with_raw(mut self, name: &str, query: &str) -> Self {
47 self.ctes.push(Cte {
48 name: name.to_string(),
49 query: CteQuery::Raw(query.to_string()),
50 });
51 self
52 }
53
54 pub fn with(mut self, name: &str, query: Select) -> Self {
55 self.ctes.push(Cte {
56 name: name.to_string(),
57 query: CteQuery::Select(query),
58 });
59 self
60 }
61
62 pub fn distinct(mut self) -> Self {
63 self.distinct = true;
64 self
65 }
66
67 pub fn table_column(mut self, table: &str, column: &str) -> Self {
68 self.columns.push(SelectColumn::table_column(table, column));
69 self
70 }
71
72 pub fn select_raw(mut self, expression: impl Into<String>) -> Self {
73 self.columns.push(SelectColumn {
74 expression: SelectExpression::Raw(expression.into()),
75 alias: None,
76 });
77 self
78 }
79
80 pub fn from(mut self, table: &str) -> Self {
81 self.from = Some(From {
82 schema: None,
83 table: table.to_string(),
84 alias: None,
85 });
86 self
87 }
88
89 pub fn join(mut self, join: Join) -> Self {
90 self.join.push(join);
91 self
92 }
93
94 pub fn where_(mut self, where_: Where) -> Self {
96 match self.where_ {
97 Where::And(ref mut v) => v.push(where_),
98 _ => self.where_ = Where::And(vec![self.where_, where_]),
99 }
100 self
101 }
102
103 pub fn where_raw(self, where_: impl Into<String>) -> Self {
104 self.where_(Where::raw(where_))
105 }
106
107 pub fn group_by(mut self, group: &str) -> Self {
108 self.group.push(GroupBy(group.to_string()));
109 self
110 }
111
112 pub fn having(mut self, having: Where) -> Self {
113 match self.having {
114 Where::And(ref mut v) => v.push(having),
115 _ => self.having = Where::And(vec![self.having, having]),
116 }
117 self
118 }
119
120 pub fn order_by(mut self, order: OrderBy) -> Self {
121 self.order.push(order);
122 self
123 }
124
125 pub fn order_asc(self, order: &str) -> Self {
126 self.order_by(OrderBy::new(order).asc())
127 }
128
129 pub fn order_desc(self, order: &str) -> Self {
130 self.order_by(OrderBy::new(order).desc())
131 }
132
133 pub fn limit(mut self, limit: usize) -> Self {
134 self.limit = Some(limit);
135 self
136 }
137
138 pub fn offset(mut self, offset: usize) -> Self {
139 self.offset = Some(offset);
140 self
141 }
142}
143
144#[derive(Debug, Clone, PartialEq, Eq)]
146pub enum SelectExpression {
147 Column {
148 schema: Option<String>,
149 table: Option<String>,
150 column: String,
151 },
152 Raw(String),
153}
154
155#[derive(Debug, Clone, PartialEq, Eq)]
157pub struct SelectColumn {
158 pub expression: SelectExpression,
159 pub alias: Option<String>,
160}
161
162impl SelectColumn {
163 pub fn column(&self) -> Option<&str> {
164 match &self.expression {
165 SelectExpression::Column { column, .. } => Some(column),
166 _ => None,
167 }
168 }
169
170 pub fn new(column: &str) -> Self {
171 Self {
172 expression: SelectExpression::Column {
173 schema: None,
174 table: None,
175 column: column.to_string(),
176 },
177 alias: None,
178 }
179 }
180
181 pub fn table_column(table: &str, column: &str) -> Self {
182 Self {
183 expression: SelectExpression::Column {
184 schema: None,
185 table: Some(table.to_string()),
186 column: column.to_string(),
187 },
188 alias: None,
189 }
190 }
191
192 pub fn raw(expression: &str) -> Self {
193 Self {
194 expression: SelectExpression::Raw(expression.to_string()),
195 alias: None,
196 }
197 }
198
199 pub fn alias(mut self, alias: impl Into<String>) -> Self {
200 self.alias = Some(alias.into());
201 self
202 }
203}
204
205impl<T: Into<String>> std::convert::From<T> for SelectColumn {
206 fn from(column: T) -> Self {
207 Self {
208 expression: SelectExpression::Column {
209 schema: None,
210 table: None,
211 column: column.into(),
212 },
213 alias: None,
214 }
215 }
216}
217
218impl ToSql for SelectColumn {
219 fn write_sql(&self, buf: &mut String, _: Dialect) {
220 use SelectExpression::*;
221 match &self.expression {
222 Column {
223 schema,
224 table,
225 column,
226 } => {
227 if let Some(schema) = schema {
228 buf.push_quoted(schema);
229 buf.push('.');
230 }
231 if let Some(table) = table {
232 buf.push_quoted(table);
233 buf.push('.');
234 }
235 buf.push_quoted(column);
236 }
237 Raw(raw) => {
238 buf.push_str(raw);
239 }
240 }
241 if let Some(alias) = &self.alias {
242 buf.push_str(" AS ");
243 buf.push_quoted(alias);
244 }
245 }
246}
247
248#[derive(Debug, Clone, PartialEq, Eq)]
249pub struct From {
250 pub schema: Option<String>,
251 pub table: String,
252 pub alias: Option<String>,
253}
254
255impl<T: Into<String>> std::convert::From<T> for From {
256 fn from(table: T) -> Self {
257 Self {
258 schema: None,
259 table: table.into(),
260 alias: None,
261 }
262 }
263}
264
265impl ToSql for From {
266 fn write_sql(&self, buf: &mut String, _: Dialect) {
267 buf.push_table_name(&self.schema, &self.table);
268 if let Some(alias) = &self.alias {
269 buf.push_str(" AS ");
270 buf.push_quoted(alias);
271 }
272 }
273}
274
275#[derive(Debug, Clone, PartialEq, Eq)]
276pub enum Where {
277 And(Vec<Where>),
278 Or(Vec<Where>),
279 #[deprecated]
280 Raw(String),
281 Expr(Expr),
282}
283
284impl std::convert::From<String> for Where {
285 fn from(s: String) -> Self {
286 Where::Expr(Expr::Raw(s))
287 }
288}
289
290impl Where {
291 pub fn is_empty(&self) -> bool {
292 use Where::*;
293 match self {
294 And(v) => v.is_empty(),
295 Or(v) => v.is_empty(),
296 #[allow(deprecated)]
297 Raw(s) => s.is_empty(),
298 Expr(_) => false,
299 }
300 }
301
302 pub fn raw(s: impl Into<String>) -> Self {
303 Where::Expr(Expr::Raw(s.into()))
304 }
305}
306
307impl ToSql for Where {
308 fn write_sql(&self, buf: &mut String, dialect: Dialect) {
309 match self {
310 Where::And(v) => {
311 buf.push_sql_sequence(&v, " AND ", dialect);
312 }
313 Where::Or(v) => {
314 buf.push('(');
315 buf.push_sql_sequence(&v, " OR ", dialect);
316 buf.push(')');
317 }
318 #[allow(deprecated)]
319 Where::Raw(s) => {
320 buf.push_str(s);
321 }
322 Where::Expr(expr) => {
323 buf.push_sql(expr, dialect);
324 }
325 }
326 }
327}
328
329#[derive(Debug, Clone, Copy, PartialEq, Eq)]
331pub enum Direction {
332 Asc,
333 Desc,
334}
335
336#[derive(Debug, Clone, Copy, PartialEq, Eq)]
337pub enum NullsOrder {
338 First,
339 Last,
340}
341
342#[derive(Debug, Clone, PartialEq, Eq)]
343pub struct OrderBy {
344 pub column: String,
345 pub direction: Option<Direction>,
346 pub nulls: Option<NullsOrder>,
347}
348
349impl OrderBy {
350 pub fn new(column: &str) -> Self {
351 OrderBy {
352 column: column.to_string(),
353 direction: None,
354 nulls: None,
355 }
356 }
357
358 pub fn direction(mut self, direction: Direction) -> Self {
359 self.direction = Some(direction);
360 self
361 }
362
363 pub fn asc(mut self) -> Self {
364 self.direction = Some(Direction::Asc);
365 self
366 }
367
368 pub fn desc(mut self) -> Self {
369 self.direction = Some(Direction::Desc);
370 self
371 }
372
373 pub fn nulls(mut self, nulls: NullsOrder) -> Self {
374 self.nulls = Some(nulls);
375 self
376 }
377
378 pub fn nulls_first(mut self) -> Self {
379 self.nulls = Some(NullsOrder::First);
380 self
381 }
382
383 pub fn nulls_last(mut self) -> Self {
384 self.nulls = Some(NullsOrder::Last);
385 self
386 }
387}
388
389impl ToSql for OrderBy {
390 fn write_sql(&self, buf: &mut String, _: Dialect) {
391 use Direction::*;
392 buf.push_str(&self.column);
393 if let Some(direction) = self.direction {
394 match direction {
395 Asc => buf.push_str(" ASC"),
396 Desc => buf.push_str(" DESC"),
397 }
398 }
399 if let Some(nulls) = self.nulls {
400 match nulls {
401 NullsOrder::First => buf.push_str(" NULLS FIRST"),
402 NullsOrder::Last => buf.push_str(" NULLS LAST"),
403 }
404 }
405 }
406}
407
408impl Default for Direction {
409 fn default() -> Self {
410 Direction::Asc
411 }
412}
413
414#[derive(Debug, Clone, PartialEq, Eq)]
415pub struct GroupBy(String);
416
417impl ToSql for GroupBy {
418 fn write_sql(&self, buf: &mut String, _: Dialect) {
419 buf.push_str(&self.0)
420 }
421}
422
423impl ToSql for Select {
424 fn write_sql(&self, buf: &mut String, dialect: Dialect) {
425 if !self.ctes.is_empty() {
426 buf.push_str("WITH ");
427 buf.push_sql_sequence(&self.ctes, ", ", dialect);
428 buf.push(' ');
429 }
430 buf.push_str("SELECT ");
431 if self.distinct {
432 buf.push_str("DISTINCT ");
433 }
434 buf.push_sql_sequence(&self.columns, ", ", dialect);
435 if let Some(from) = &self.from {
436 buf.push_str(" FROM ");
437 buf.push_str(&from.to_sql(dialect));
438 buf.push(' ');
439 }
440 if !self.join.is_empty() {
441 buf.push_sql_sequence(&self.join, " ", dialect);
442 }
443 if !self.where_.is_empty() {
444 buf.push_str(" WHERE ");
445 buf.push_str(&self.where_.to_sql(dialect));
446 }
447 if !self.group.is_empty() {
448 buf.push_str(" GROUP BY ");
449 buf.push_sql_sequence(&self.group, ", ", dialect);
450 }
451 if !self.having.is_empty() {
452 buf.push_str(" HAVING ");
453 buf.push_str(&self.having.to_sql(dialect));
454 }
455 if !self.order.is_empty() {
456 buf.push_str(" ORDER BY ");
457 buf.push_sql_sequence(&self.order, ", ", dialect);
458 }
459 if let Some(limit) = self.limit {
460 buf.push_str(" LIMIT ");
461 buf.push_str(&limit.to_string());
462 }
463 if let Some(offset) = self.offset {
464 buf.push_str(" OFFSET ");
465 buf.push_str(&offset.to_string());
466 }
467 }
468}
469
470#[cfg(test)]
471mod tests {
472 use super::*;
473
474 #[test]
475 fn test_basic() {
476 let select = Select::default()
477 .with_raw("foo", "SELECT 1")
478 .with("bar", Select::default().select_raw("1"))
479 .select_raw("id")
480 .select_raw("name")
481 .from("users")
482 .join(Join::new("posts").on_raw("users.id = posts.user_id"))
483 .where_raw("1=1")
484 .order_asc("id")
485 .order_desc("name")
486 .limit(10)
487 .offset(5);
488 assert_eq!(
489 select.to_sql(Dialect::Postgres),
490 r#"WITH foo AS (SELECT 1), bar AS (SELECT 1) SELECT id, name FROM "users" JOIN "posts" ON users.id = posts.user_id WHERE 1=1 ORDER BY id ASC, name DESC LIMIT 10 OFFSET 5"#
491 );
492 }
493}