1pub(crate) use crate::clause::Clause;
2use crate::*;
3use tokio_postgres::types::ToSql;
4
5pub struct Where;
6pub struct Order;
7pub struct SelectCondition<'q> {
8 pub(crate) sql: String,
9 pub(crate) params: Vec<&'q (dyn ToSql + Sync)>,
10}
11impl<'q, T> SqlClause<'q> for Clause<'q, T> {
17 fn unwrap(self) -> (String, Vec<&'q (dyn ToSql + Sync)>) {
18 (self.sql, self.params)
19 }
20}
21
22impl<'q> Clause<'q, Where> {
23 pub fn limit(self, limit: &'q i64) -> Clause<'q, Paging> {
24 __limit(self, limit)
25 }
26 pub fn offset(self, offset: &'q i64) -> Clause<'q, Paging> {
27 __offset(self, offset)
28 }
29}
30
31impl<'q> Clause<'q, Paging> {
32 pub fn limit(self, limit: &'q i64) -> Clause<'q, ()> {
33 __limit(self, limit)
34 }
35 pub fn offset(self, offset: &'q i64) -> Clause<'q, ()> {
36 __offset(self, offset)
37 }
38}
39
40impl<'q> Clause<'q, Order> {
41 pub fn limit(self, limit: &'q i64) -> Clause<'q, Paging> {
42 __limit(self, limit)
43 }
44 pub fn offset(self, offset: &'q i64) -> Clause<'q, Paging> {
45 __offset(self, offset)
46 }
47}
48
49impl<'q> Clause<'q, Select> {
50 pub fn select<T>(self, clause: Clause<'q, T>) -> Clause<'q, Select> {
51 let mut sql = self.sql;
52 let mut params = self.params;
53 sql.push_str(", ");
54 sql.push_str(&clause.sql);
55 params.extend_from_slice(&clause.params);
56 Clause::new(sql, params)
57 }
58 pub fn from(self, table: &str) -> Clause<'q, From> {
59 let mut sql = self.sql;
60 sql.push_str(" FROM ");
61 sql.push_str(&format_prim(table));
62 Clause::new(sql, self.params)
63 }
64}
65
66fn __join<'q, T>(clause: Clause<'q, T>, join: &str, table: &str) -> Clause<'q, Join> {
67 let (mut sql, params) = clause.unwrap();
68 sql.push_str(&format!(" {join}JOIN "));
69 sql.push_str(&format_prim(table));
70 Clause::new(sql, params)
71}
72impl<'q> Clause<'q, From> {
73 pub fn join(self, table: &str) -> Clause<'q, Join> {
74 __join(self, "", table)
75 }
76 pub fn left_join(self, table: &str) -> Clause<'q, Join> {
77 __join(self, "LEFT ", table)
78 }
79
80 pub fn left_outer_join(self, table: &str) -> Clause<'q, Join> {
81 __join(self, "LEFT OUTER ", table)
82 }
83
84 pub fn right_join(self, table: &str) -> Clause<'q, Join> {
85 __join(self, "RIGHT ", table)
86 }
87
88 pub fn right_outer_join(self, table: &str) -> Clause<'q, Join> {
89 __join(self, "RIGHT OUTER ", table)
90 }
91
92 pub fn where_col(self, name: &str, condition: Clause<'q, Condition>) -> Clause<'q, Where> {
93 __where(self, name, condition)
94 }
95 pub fn where_condition(self, _condition: Clause<'q, Condition>) -> Clause<'q, Where> {
96 todo!()
98 }
99
100 pub fn limit(self, limit: &'q i64) -> Clause<'q, ()> {
101 __limit(self, limit)
102 }
103 pub fn offset(self, offset: &'q i64) -> Clause<'q, ()> {
104 __offset(self, offset)
105 }
106}
107fn __limit<'q, T, C>(clause: Clause<'q, T>, limit: &'q i64) -> Clause<'q, C> {
108 let (mut sql, mut params) = clause.unwrap();
109 sql.push_str(" LIMIT $");
110 params.push(limit);
111 Clause::new(sql, params)
112}
113fn __offset<'q, T, C>(clause: Clause<'q, T>, offset: &'q i64) -> Clause<'q, C> {
114 let (mut sql, mut params) = clause.unwrap();
115 sql.push_str(" OFFSET $");
116 params.push(offset);
117 Clause::new(sql, params)
118}
119
120impl<'q> Clause<'q, Join> {
121 pub fn using(self, cols: Clause<Column>) -> Clause<'q, From> {
122 let (mut sql, params) = self.unwrap();
123 let (col_sql, _) = cols.unwrap();
124 sql.push_str(" using(");
125 sql.push_str(&col_sql);
126 sql.push(')');
127 Clause::new(sql, params)
128 }
129 pub fn using_col(self, col_name: &str) -> Clause<'q, From> {
130 let (mut sql, params) = self.unwrap();
131 sql.push_str(" using(");
132 sql.push_str(&format_col(col_name));
133 sql.push(')');
134 Clause::new(sql, params)
135 }
136 pub fn using_cols(self, col_names: &[&str]) -> Clause<'q, From> {
137 let (mut sql, params) = self.unwrap();
138 sql.push_str(" using(");
139 __cols(&mut sql, col_names);
140 sql.push(')');
141 Clause::new(sql, params)
142 }
143 pub fn on(self, col1: &str, col2: &str) -> Clause<'q, From> {
144 let (mut sql, params) = self.unwrap();
145 sql.push_str(" on ");
146 sql.push_str(&format_col(col1));
147 sql.push_str(" = ");
148 sql.push_str(&format_col(col2));
149 Clause::new(sql, params)
150 }
151 pub fn on_wrap(self, condition: Clause<'q, Condition>) -> Clause<'q, From> {
152 let (mut sql, params) = self.unwrap();
153 let (cond_sql, _) = condition.unwrap();
154 sql.push_str(" on (");
155 sql.push_str(&cond_sql);
156 sql.push(')');
157 Clause::new(sql, params)
158 }
159}
160
161pub(crate) fn __where<'q, T, C>(
162 clause: Clause<'q, T>,
163 name: &str,
164 condition: Clause<'q, Condition>,
165) -> Clause<'q, C> {
166 let mut sql = clause.sql;
167 let mut params = clause.params;
168 sql.push_str(" where ");
169 sql.push_str(&format_col(name));
170 let (cond_sql, cond_params) = condition.unwrap();
171 sql.push_str(cond_sql.as_str());
172 params.extend(cond_params);
173 Clause::new(sql, params)
174}
175
176impl<'q> Clause<'q, Where> {
177 pub fn and(self, name: &str, condition: Clause<'q, Condition>) -> Clause<'q, Where> {
178 __and(self, name, condition)
179 }
188 pub fn or(self, name: &str, condition: Clause<'q, Condition>) -> Clause<'q, Where> {
189 __or(self, name, condition)
190 }
199
200 pub fn and_wrap(
201 self,
202 clause: Clause<'q, Where>,
204 ) -> Clause<'q, Where> {
205 __and_wrap(self, clause)
206 }
215
216 pub fn or_wrap(self, clause: Clause<'q, Where>) -> Clause<'q, Where> {
217 __or_wrap(self, clause)
218 }
227
228 pub fn order_by_cols(self, columns: &[&str]) -> Clause<'q, Order> {
229 let (mut sql, params) = self.unwrap();
230 let mut not_first = false;
231 sql.push_str(" order by ");
232 for col in columns {
233 if not_first {
234 sql.push_str(", ");
235 } else {
236 not_first = true;
237 }
238 sql.push_str(&__order(col));
239 }
240 Clause::new(sql, params)
241 }
242}
243fn __order(col: &str) -> String {
244 let (col_name, dir) = {
245 let mut split_as = col.split(" ");
246 let col_name = split_as.next().unwrap_or_default();
247 let alias = split_as.next();
248 (wrap(col_name), alias)
249 };
250 if let Some(dir) = dir
251 && dir.eq_ignore_ascii_case("desc")
252 {
253 format!("{} {}", col_name, dir)
254 } else {
255 format!("{}", col_name)
256 }
257}
258
259pub fn select(clause: Clause<Column>) -> Clause<Select> {
262 Clause::new(format!("select {}", clause.sql), clause.params)
263}
264
265pub fn select_all<'q>() -> Clause<'q, Select> {
266 Clause::new("select *".to_string(), Vec::new())
267}
268
269pub fn col<'q>(name: &str) -> Clause<'q, Column> {
270 Clause::new(format_prim(name), vec![])
271}
272
273pub fn val<'q, T: ToSql + Sync + 'q, S>(value: &'q T) -> Clause<'q, S> {
274 Clause::new("$".to_string(), vec![value])
275}
276pub fn where_col<'q>(name: &str, condition: Clause<'q, Condition>) -> Clause<'q, Where> {
277 let mut sql = String::new();
278 let (cond_sql, cond_params) = condition.unwrap();
279 sql.push(' ');
280 sql.push_str(&format_col(name));
281 sql.push_str(cond_sql.as_str());
282 Clause::new(sql, cond_params)
283}
284
285#[cfg(test)]
286mod tests {
287 use super::*;
288 use tokio_postgres::NoTls;
289
290 #[test]
291 fn example() {
292
293 let user_id: i32 = 123;
294 let limit: i64 = 10;
295 let offset: i64 = 50;
296 let (sql, params) = select(cols(&["u.UserId as Id", "u.Name", "p.Title", "p.Content"]))
297 .from("User as u")
298 .left_join("Post as p")
299 .using_col("UserId")
300 .where_col("u.UserId", eq(&user_id))
301 .order_by_cols(&["CreatedAt desc"])
302 .limit(&limit)
303 .offset(&offset)
304 .build();
305
306 println!("SQL: {}", sql);
307 println!("Params: {:?}", params);
308 }
310 #[test]
311 fn select_test() {
312 let user_id: i32 = 123;
313 let (sql, params) = select(cols(&["u.UserId", "u.Name", "p.Title", "p.Content"]))
314 .from("User as u")
315 .left_join("Post as p")
316 .using_col("UserId")
317 .where_col("u.UserId", eq(&user_id))
318 .and("t.Value", lt(&1000))
319 .and_wrap(where_col("t.Asd", eq(&true)).or("Xwe", eq(&false)))
320 .order_by_cols(&["CreatedAt desc"])
321 .limit(&10)
322 .offset(&100)
323 .build();
324 println!("{}", sql);
326 println!("{:?}", params);
327 }
328
329 #[tokio::test]
330 async fn it_works() {
331 const CONN_STR: &str = "postgresql://mioto:H7qCNYKJ6AR3YuD@procyon.exa.mioto.id:16384/GPS_General?connect_timeout=60";
333 let (client, _x) = tokio_postgres::connect(CONN_STR, NoTls).await.unwrap();
334 tokio::spawn(async move {
335 _x.await.unwrap();
336 });
337
338 let (sql, params) = select(cols(&[
339 "intIdKendaraan",
340 "intIdKlien",
341 "intIdStorage",
342 "strNoImei",
343 ]))
344 .from("Kendaraan")
345 .limit(&1)
346 .build();
347 println!("{}", sql);
348 println!("params: {:?}", params);
349
350 let result = client.query(&sql, ¶ms).await.unwrap();
351 println!("{:?}", result);
359 }
360
361 #[test]
362 fn branched() {
363 let opt1 = Some(1);
364 let mut query = select(cols(&["u.UserId", "u.Name", "p.Title", "p.Content"]))
365 .from("User as u")
366 .where_col("u.UserId", eq(&opt1));
367 if let Some(val) = &opt1 {
368 query = query.and("u.PostId", eq(val));
369 }
370 query.and("u.Title", eq(&1));
371 }
372}