1use std::{
2 future::{Future, IntoFuture},
3 marker::PhantomData,
4 ops::Deref,
5 pin::Pin,
6};
7
8use tokio_postgres::{types::ToSql, Row};
9
10use super::{Executable, PushChunk, Query, ToQuery, Where};
11use crate::Column;
12
13pub struct Select<'a, T = Vec<Row>> {
16 cols: Vec<Column>,
17 from: &'static str,
18 where_: Where<'a>,
19 marker: PhantomData<T>,
20 limit: Option<u64>,
21 offset: Option<u64>,
22}
23
24impl<'a, T> ToQuery<'a, T> for Select<'a, T> {}
25
26impl<'a, T> Select<'a, T> {
27 #[doc(hidden)]
28 pub fn new(cols: &[&dyn Deref<Target = Column>], from: &'static str) -> Select<'a, T> {
29 Select {
30 cols: cols.iter().map(|i| (***i)).collect(),
31 from,
32 where_: Where::Empty,
33 marker: PhantomData::<T>,
34 limit: None,
35 offset: None,
36 }
37 }
38
39 pub fn where_(mut self, where_: Where<'a>) -> Select<'a, T> {
44 if self.where_.is_empty() {
45 self.where_ = where_;
46 } else {
47 self.where_ = self.where_.and(where_);
48 }
49
50 self
51 }
52
53 pub fn where_raw(
71 self,
72 statement: impl Into<String>,
73 params: Vec<&'a (dyn ToSql + Sync)>,
74 ) -> Select<'a, T> {
75 let where_ = Where::new(statement.into(), params);
76
77 self.where_(where_)
78 }
79
80 pub fn limit(mut self, limit: u64) -> Select<'a, T> {
82 self.limit = Some(limit);
83
84 self
85 }
86
87 pub fn offset(mut self, offset: u64) -> Select<'a, T> {
89 self.offset = Some(offset);
90
91 self
92 }
93}
94
95impl<'a, T> PushChunk<'a> for Select<'a, T> {
96 fn push_to_buffer<B>(&mut self, buffer: &mut Query<'a, B>) {
97 buffer.0.push_str("SELECT ");
98
99 let cols = self
101 .cols
102 .iter()
103 .map(|i| i.full_name())
104 .collect::<Vec<_>>()
105 .join(", ");
106 buffer.0.push_str(&cols);
107
108 buffer.0.push_str(" FROM ");
111 buffer.0.push_str(self.from);
112
113 if !self.where_.is_empty() {
115 buffer.0.push_str(" WHERE ");
116 self.where_.push_to_buffer(buffer);
117 }
118
119 if let Some(limit) = self.limit {
121 buffer.0.push_str(" LIMIT ");
122 buffer.0.push_str(&limit.to_string());
123 }
124
125 if let Some(offset) = self.offset {
127 buffer.0.push_str(" OFFSET ");
128 buffer.0.push_str(&offset.to_string())
129 }
130 }
131}
132
133impl<'a, T: Sync + Send + 'a> IntoFuture for Select<'a, T>
134where
135 Select<'a, T>: ToQuery<'a, T>,
136 Query<'a, T>: Executable<Output = T>,
137{
138 type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + 'a>>;
139 type Output = Result<T, crate::Error>;
140
141 fn into_future(mut self) -> Self::IntoFuture {
142 let query = self.to_query();
143 Box::pin(async move { query.exec().await })
144 }
145}
146
147#[cfg(test)]
148mod test {
149 #![allow(dead_code)]
150
151 use crate::prelude::*;
152
153 #[derive(Model)]
154 struct Book {
155 #[column(primary_key, auto)]
156 id: i64,
157 title: String,
158 }
159
160 #[test]
161 fn select_limit() {
162 let query = Book::select().limit(3).to_query().0;
163 assert_eq!(query, "SELECT book.id, book.title FROM book LIMIT 3");
164 }
165
166 #[test]
167 fn select_offset() {
168 let query = Book::select().offset(4).to_query().0;
169 assert_eq!(query, "SELECT book.id, book.title FROM book OFFSET 4");
170 }
171}