1use super::{
6 condition::{Condition, Order},
7 entity::{Entity, ToRow},
8};
9use std::marker::PhantomData;
10use wae_types::Value;
11
12#[cfg(feature = "turso")]
13use crate::types::from_wae_value;
14#[cfg(feature = "turso")]
15use turso::Value as TursoValue;
16
17#[cfg(feature = "mysql")]
18use crate::types::from_wae_to_mysql;
19#[cfg(feature = "mysql")]
20use mysql_async::Value as MySqlValue;
21
22#[derive(Debug, Clone, Copy)]
24pub enum JoinType {
25 Inner,
27 Left,
29 Right,
31 Full,
33}
34
35#[derive(Debug, Clone)]
37pub struct Join {
38 join_type: JoinType,
40 table: String,
42 on: Condition,
44}
45
46impl Join {
47 pub fn new(join_type: JoinType, table: String, on: Condition) -> Self {
49 Self { join_type, table, on }
50 }
51}
52
53pub struct SelectBuilder<E: Entity> {
55 columns: Vec<String>,
56 joins: Vec<Join>,
57 conditions: Vec<Condition>,
58 group_by: Vec<String>,
59 having: Vec<Condition>,
60 order_by: Vec<(String, Order)>,
61 limit: Option<u64>,
62 offset: Option<u64>,
63 _marker: PhantomData<E>,
64}
65
66impl<E: Entity> SelectBuilder<E> {
67 pub(crate) fn new() -> Self {
68 Self {
69 columns: Vec::new(),
70 joins: Vec::new(),
71 conditions: Vec::new(),
72 group_by: Vec::new(),
73 having: Vec::new(),
74 order_by: Vec::new(),
75 limit: None,
76 offset: None,
77 _marker: PhantomData,
78 }
79 }
80
81 pub fn columns(mut self, columns: &[&str]) -> Self {
83 self.columns = columns.iter().map(|s| s.to_string()).collect();
84 self
85 }
86
87 pub fn join(mut self, join_type: JoinType, table: &str, on: Condition) -> Self {
89 self.joins.push(Join::new(join_type, table.to_string(), on));
90 self
91 }
92
93 pub fn inner_join(mut self, table: &str, on: Condition) -> Self {
95 self.joins.push(Join::new(JoinType::Inner, table.to_string(), on));
96 self
97 }
98
99 pub fn left_join(mut self, table: &str, on: Condition) -> Self {
101 self.joins.push(Join::new(JoinType::Left, table.to_string(), on));
102 self
103 }
104
105 pub fn right_join(mut self, table: &str, on: Condition) -> Self {
107 self.joins.push(Join::new(JoinType::Right, table.to_string(), on));
108 self
109 }
110
111 pub fn full_join(mut self, table: &str, on: Condition) -> Self {
113 self.joins.push(Join::new(JoinType::Full, table.to_string(), on));
114 self
115 }
116
117 pub fn where_(mut self, condition: Condition) -> Self {
119 self.conditions.push(condition);
120 self
121 }
122
123 pub fn where_all(mut self, conditions: Vec<Condition>) -> Self {
125 self.conditions.extend(conditions);
126 self
127 }
128
129 pub fn group_by(mut self, columns: &[&str]) -> Self {
131 self.group_by = columns.iter().map(|s| s.to_string()).collect();
132 self
133 }
134
135 pub fn having(mut self, condition: Condition) -> Self {
137 self.having.push(condition);
138 self
139 }
140
141 pub fn having_all(mut self, conditions: Vec<Condition>) -> Self {
143 self.having.extend(conditions);
144 self
145 }
146
147 pub fn order_by<C: Into<String>>(mut self, column: C, order: Order) -> Self {
149 self.order_by.push((column.into(), order));
150 self
151 }
152
153 pub fn limit(mut self, limit: u64) -> Self {
155 self.limit = Some(limit);
156 self
157 }
158
159 pub fn offset(mut self, offset: u64) -> Self {
161 self.offset = Some(offset);
162 self
163 }
164
165 #[cfg(feature = "turso")]
166 pub(crate) fn build_turso(&self) -> (String, Vec<TursoValue>) {
168 let columns = if self.columns.is_empty() { "*".to_string() } else { self.columns.join(", ") };
169
170 let mut sql = format!("SELECT {} FROM {}", columns, E::table_name());
171 let mut params = Vec::new();
172
173 for join in &self.joins {
174 let (on_sql, on_params) = join.on.build_turso();
175 let join_type_str = match join.join_type {
176 JoinType::Inner => "INNER JOIN",
177 JoinType::Left => "LEFT JOIN",
178 JoinType::Right => "RIGHT JOIN",
179 JoinType::Full => "FULL JOIN",
180 };
181 sql.push_str(&format!(" {} {} ON {}", join_type_str, join.table, on_sql));
182 params.extend(on_params);
183 }
184
185 if !self.conditions.is_empty() {
186 let where_conditions = self.conditions.to_vec();
187 if where_conditions.len() == 1 {
188 let (cond_sql, cond_params) = where_conditions[0].build_turso();
189 sql.push_str(&format!(" WHERE {}", cond_sql));
190 params.extend(cond_params);
191 }
192 else {
193 let (cond_sql, cond_params) = Condition::and(where_conditions).build_turso();
194 sql.push_str(&format!(" WHERE {}", cond_sql));
195 params.extend(cond_params);
196 }
197 }
198
199 if !self.group_by.is_empty() {
200 sql.push_str(&format!(" GROUP BY {}", self.group_by.join(", ")));
201 }
202
203 if !self.having.is_empty() {
204 let having_conditions = self.having.to_vec();
205 if having_conditions.len() == 1 {
206 let (cond_sql, cond_params) = having_conditions[0].build_turso();
207 sql.push_str(&format!(" HAVING {}", cond_sql));
208 params.extend(cond_params);
209 }
210 else {
211 let (cond_sql, cond_params) = Condition::and(having_conditions).build_turso();
212 sql.push_str(&format!(" HAVING {}", cond_sql));
213 params.extend(cond_params);
214 }
215 }
216
217 if !self.order_by.is_empty() {
218 let order_parts: Vec<String> = self
219 .order_by
220 .iter()
221 .map(|(col, order)| {
222 format!(
223 "{} {}",
224 col,
225 match order {
226 Order::Asc => "ASC",
227 Order::Desc => "DESC",
228 }
229 )
230 })
231 .collect();
232 sql.push_str(&format!(" ORDER BY {}", order_parts.join(", ")));
233 }
234
235 if let Some(limit) = self.limit {
236 sql.push_str(&format!(" LIMIT {}", limit));
237 }
238
239 if let Some(offset) = self.offset {
240 sql.push_str(&format!(" OFFSET {}", offset));
241 }
242
243 (sql, params)
244 }
245
246 #[cfg(feature = "mysql")]
247 #[allow(dead_code)]
248 pub(crate) fn build_mysql(&self) -> (String, Vec<MySqlValue>) {
250 let columns = if self.columns.is_empty() { "*".to_string() } else { self.columns.join(", ") };
251
252 let mut sql = format!("SELECT {} FROM {}", columns, E::table_name());
253 let mut params = Vec::new();
254
255 for join in &self.joins {
256 let (on_sql, on_params) = join.on.build_mysql();
257 let join_type_str = match join.join_type {
258 JoinType::Inner => "INNER JOIN",
259 JoinType::Left => "LEFT JOIN",
260 JoinType::Right => "RIGHT JOIN",
261 JoinType::Full => "FULL JOIN",
262 };
263 sql.push_str(&format!(" {} {} ON {}", join_type_str, join.table, on_sql));
264 params.extend(on_params);
265 }
266
267 if !self.conditions.is_empty() {
268 let where_conditions = self.conditions.to_vec();
269 if where_conditions.len() == 1 {
270 let (cond_sql, cond_params) = where_conditions[0].build_mysql();
271 sql.push_str(&format!(" WHERE {}", cond_sql));
272 params.extend(cond_params);
273 }
274 else {
275 let (cond_sql, cond_params) = Condition::and(where_conditions).build_mysql();
276 sql.push_str(&format!(" WHERE {}", cond_sql));
277 params.extend(cond_params);
278 }
279 }
280
281 if !self.group_by.is_empty() {
282 sql.push_str(&format!(" GROUP BY {}", self.group_by.join(", ")));
283 }
284
285 if !self.having.is_empty() {
286 let having_conditions = self.having.to_vec();
287 if having_conditions.len() == 1 {
288 let (cond_sql, cond_params) = having_conditions[0].build_mysql();
289 sql.push_str(&format!(" HAVING {}", cond_sql));
290 params.extend(cond_params);
291 }
292 else {
293 let (cond_sql, cond_params) = Condition::and(having_conditions).build_mysql();
294 sql.push_str(&format!(" HAVING {}", cond_sql));
295 params.extend(cond_params);
296 }
297 }
298
299 if !self.order_by.is_empty() {
300 let order_parts: Vec<String> = self
301 .order_by
302 .iter()
303 .map(|(col, order)| {
304 format!(
305 "{} {}",
306 col,
307 match order {
308 Order::Asc => "ASC",
309 Order::Desc => "DESC",
310 }
311 )
312 })
313 .collect();
314 sql.push_str(&format!(" ORDER BY {}", order_parts.join(", ")));
315 }
316
317 if let Some(limit) = self.limit {
318 sql.push_str(&format!(" LIMIT {}", limit));
319 }
320
321 if let Some(offset) = self.offset {
322 sql.push_str(&format!(" OFFSET {}", offset));
323 }
324
325 (sql, params)
326 }
327
328 #[cfg(feature = "postgres")]
329 pub(crate) fn build_postgres(&self) -> (String, Vec<crate::connection::postgres::PostgresParam>) {
331 let columns = if self.columns.is_empty() { "*".to_string() } else { self.columns.join(", ") };
332
333 let mut sql = format!("SELECT {} FROM {}", columns, E::table_name());
334 let mut params = Vec::new();
335 let mut param_offset = 0;
336
337 for join in &self.joins {
338 let (on_sql, on_params) = join.on.build_postgres();
339 let on_sql = replace_placeholders_for_query(&on_sql, param_offset + 1);
340 let join_type_str = match join.join_type {
341 JoinType::Inner => "INNER JOIN",
342 JoinType::Left => "LEFT JOIN",
343 JoinType::Right => "RIGHT JOIN",
344 JoinType::Full => "FULL JOIN",
345 };
346 sql.push_str(&format!(" {} {} ON {}", join_type_str, join.table, on_sql));
347 let on_params_len = on_params.len();
348 params.extend(on_params);
349 param_offset += on_params_len;
350 }
351
352 if !self.conditions.is_empty() {
353 let where_conditions = self.conditions.to_vec();
354 let (cond_sql, cond_params) = if where_conditions.len() == 1 {
355 where_conditions[0].build_postgres()
356 }
357 else {
358 Condition::and(where_conditions).build_postgres()
359 };
360 let cond_sql = replace_placeholders_for_query(&cond_sql, param_offset + 1);
361 sql.push_str(&format!(" WHERE {}", cond_sql));
362 let cond_params_len = cond_params.len();
363 params.extend(cond_params);
364 param_offset += cond_params_len;
365 }
366
367 if !self.group_by.is_empty() {
368 sql.push_str(&format!(" GROUP BY {}", self.group_by.join(", ")));
369 }
370
371 if !self.having.is_empty() {
372 let having_conditions = self.having.to_vec();
373 let (cond_sql, cond_params) = if having_conditions.len() == 1 {
374 having_conditions[0].build_postgres()
375 }
376 else {
377 Condition::and(having_conditions).build_postgres()
378 };
379 let cond_sql = replace_placeholders_for_query(&cond_sql, param_offset + 1);
380 sql.push_str(&format!(" HAVING {}", cond_sql));
381 params.extend(cond_params);
382 }
383
384 if !self.order_by.is_empty() {
385 let order_parts: Vec<String> = self
386 .order_by
387 .iter()
388 .map(|(col, order)| {
389 format!(
390 "{} {}",
391 col,
392 match order {
393 Order::Asc => "ASC",
394 Order::Desc => "DESC",
395 }
396 )
397 })
398 .collect();
399 sql.push_str(&format!(" ORDER BY {}", order_parts.join(", ")));
400 }
401
402 if let Some(limit) = self.limit {
403 sql.push_str(&format!(" LIMIT {}", limit));
404 }
405
406 if let Some(offset) = self.offset {
407 sql.push_str(&format!(" OFFSET {}", offset));
408 }
409
410 (sql, params)
411 }
412}
413
414#[cfg(feature = "postgres")]
415fn replace_placeholders_for_query(sql: &str, start_index: usize) -> String {
416 let mut result = String::new();
417 let mut chars = sql.chars().peekable();
418 while let Some(c) = chars.next() {
419 if c == '$' {
420 let mut num_str = String::new();
421 while let Some(&next_c) = chars.peek() {
422 if next_c.is_ascii_digit() {
423 num_str.push(next_c);
424 chars.next();
425 }
426 else {
427 break;
428 }
429 }
430 if !num_str.is_empty() {
431 if let Ok(num) = num_str.parse::<usize>() {
432 result.push_str(&format!("${}", num + start_index - 1));
433 }
434 }
435 else {
436 result.push(c);
437 }
438 }
439 else {
440 result.push(c);
441 }
442 }
443 result
444}
445
446pub struct InsertBuilder<E: Entity> {
448 data: Vec<(&'static str, Value)>,
449 _marker: PhantomData<E>,
450}
451
452impl<E: Entity> InsertBuilder<E> {
453 pub(crate) fn new() -> Self {
454 Self { data: Vec::new(), _marker: PhantomData }
455 }
456
457 pub fn from_entity<T: ToRow>(entity: &T) -> Self {
459 Self { data: entity.to_row(), _marker: PhantomData }
460 }
461
462 pub fn value(mut self, column: &'static str, value: Value) -> Self {
464 self.data.push((column, value));
465 self
466 }
467
468 pub fn values(mut self, data: Vec<(&'static str, Value)>) -> Self {
470 self.data.extend(data);
471 self
472 }
473
474 #[cfg(feature = "turso")]
475 pub(crate) fn build_turso(&self) -> (String, Vec<TursoValue>) {
477 let columns: Vec<&str> = self.data.iter().map(|(col, _)| *col).collect();
478 let placeholders: Vec<&str> = self.data.iter().map(|_| "?").collect();
479 let params: Vec<TursoValue> = self.data.iter().map(|(_, val)| from_wae_value(val.clone())).collect();
480
481 let sql = format!("INSERT INTO {} ({}) VALUES ({})", E::table_name(), columns.join(", "), placeholders.join(", "));
482
483 (sql, params)
484 }
485
486 #[cfg(feature = "mysql")]
487 pub(crate) fn build_mysql(&self) -> (String, Vec<MySqlValue>) {
489 let columns: Vec<&str> = self.data.iter().map(|(col, _)| *col).collect();
490 let placeholders: Vec<&str> = self.data.iter().map(|_| "?").collect();
491 let params: Vec<MySqlValue> = self.data.iter().map(|(_, val)| from_wae_to_mysql(val.clone())).collect();
492
493 let sql = format!("INSERT INTO {} ({}) VALUES ({})", E::table_name(), columns.join(", "), placeholders.join(", "));
494
495 (sql, params)
496 }
497}
498
499pub struct UpdateBuilder<E: Entity> {
501 data: Vec<(&'static str, Value)>,
502 conditions: Vec<Condition>,
503 _marker: PhantomData<E>,
504}
505
506impl<E: Entity> UpdateBuilder<E> {
507 pub(crate) fn new() -> Self {
508 Self { data: Vec::new(), conditions: Vec::new(), _marker: PhantomData }
509 }
510
511 pub fn set(mut self, column: &'static str, value: Value) -> Self {
513 self.data.push((column, value));
514 self
515 }
516
517 pub fn set_all(mut self, data: Vec<(&'static str, Value)>) -> Self {
519 self.data.extend(data);
520 self
521 }
522
523 pub fn from_entity<T: ToRow + Entity>(entity: &T) -> Self {
525 let id_col = T::id_column();
526 let data: Vec<(&'static str, Value)> = entity.to_row().into_iter().filter(|(col, _)| *col != id_col).collect();
527 Self { data, conditions: Vec::new(), _marker: PhantomData }
528 }
529
530 pub fn where_(mut self, condition: Condition) -> Self {
532 self.conditions.push(condition);
533 self
534 }
535
536 pub fn where_id(mut self, id: E::Id) -> Self {
538 self.conditions.push(Condition::eq(E::id_column(), id));
539 self
540 }
541
542 #[cfg(feature = "turso")]
543 pub(crate) fn build_turso(&self) -> (String, Vec<TursoValue>) {
545 let set_parts: Vec<String> = self.data.iter().map(|(col, _)| format!("{} = ?", col)).collect();
546 let mut params: Vec<TursoValue> = self.data.iter().map(|(_, val)| from_wae_value(val.clone())).collect();
547
548 let mut sql = format!("UPDATE {} SET {}", E::table_name(), set_parts.join(", "));
549
550 if !self.conditions.is_empty() {
551 let where_conditions = self.conditions.to_vec();
552 if where_conditions.len() == 1 {
553 let (cond_sql, cond_params) = where_conditions[0].build_turso();
554 sql.push_str(&format!(" WHERE {}", cond_sql));
555 params.extend(cond_params);
556 }
557 else {
558 let (cond_sql, cond_params) = Condition::and(where_conditions).build_turso();
559 sql.push_str(&format!(" WHERE {}", cond_sql));
560 params.extend(cond_params);
561 }
562 }
563
564 (sql, params)
565 }
566
567 #[cfg(feature = "mysql")]
568 pub(crate) fn build_mysql(&self) -> (String, Vec<MySqlValue>) {
570 let set_parts: Vec<String> = self.data.iter().map(|(col, _)| format!("{} = ?", col)).collect();
571 let mut params: Vec<MySqlValue> = self.data.iter().map(|(_, val)| from_wae_to_mysql(val.clone())).collect();
572
573 let mut sql = format!("UPDATE {} SET {}", E::table_name(), set_parts.join(", "));
574
575 if !self.conditions.is_empty() {
576 let where_conditions = self.conditions.to_vec();
577 if where_conditions.len() == 1 {
578 let (cond_sql, cond_params) = where_conditions[0].build_mysql();
579 sql.push_str(&format!(" WHERE {}", cond_sql));
580 params.extend(cond_params);
581 }
582 else {
583 let (cond_sql, cond_params) = Condition::and(where_conditions).build_mysql();
584 sql.push_str(&format!(" WHERE {}", cond_sql));
585 params.extend(cond_params);
586 }
587 }
588
589 (sql, params)
590 }
591}
592
593pub struct DeleteBuilder<E: Entity> {
595 conditions: Vec<Condition>,
596 _marker: PhantomData<E>,
597}
598
599impl<E: Entity> DeleteBuilder<E> {
600 pub(crate) fn new() -> Self {
601 Self { conditions: Vec::new(), _marker: PhantomData }
602 }
603
604 pub fn where_(mut self, condition: Condition) -> Self {
606 self.conditions.push(condition);
607 self
608 }
609
610 pub fn where_id(mut self, id: E::Id) -> Self {
612 self.conditions.push(Condition::eq(E::id_column(), id));
613 self
614 }
615
616 #[cfg(feature = "turso")]
617 pub(crate) fn build_turso(&self) -> (String, Vec<TursoValue>) {
619 let mut sql = format!("DELETE FROM {}", E::table_name());
620 let mut params = Vec::new();
621
622 if !self.conditions.is_empty() {
623 let where_conditions = self.conditions.to_vec();
624 if where_conditions.len() == 1 {
625 let (cond_sql, cond_params) = where_conditions[0].build_turso();
626 sql.push_str(&format!(" WHERE {}", cond_sql));
627 params.extend(cond_params);
628 }
629 else {
630 let (cond_sql, cond_params) = Condition::and(where_conditions).build_turso();
631 sql.push_str(&format!(" WHERE {}", cond_sql));
632 params.extend(cond_params);
633 }
634 }
635
636 (sql, params)
637 }
638
639 #[cfg(feature = "mysql")]
640 pub(crate) fn build_mysql(&self) -> (String, Vec<MySqlValue>) {
642 let mut sql = format!("DELETE FROM {}", E::table_name());
643 let mut params = Vec::new();
644
645 if !self.conditions.is_empty() {
646 let where_conditions = self.conditions.to_vec();
647 if where_conditions.len() == 1 {
648 let (cond_sql, cond_params) = where_conditions[0].build_mysql();
649 sql.push_str(&format!(" WHERE {}", cond_sql));
650 params.extend(cond_params);
651 }
652 else {
653 let (cond_sql, cond_params) = Condition::and(where_conditions).build_mysql();
654 sql.push_str(&format!(" WHERE {}", cond_sql));
655 params.extend(cond_params);
656 }
657 }
658
659 (sql, params)
660 }
661}
662
663pub struct QueryBuilder;
665
666impl QueryBuilder {
667 pub fn select<E: Entity>() -> SelectBuilder<E> {
669 SelectBuilder::new()
670 }
671
672 pub fn insert<E: Entity>() -> InsertBuilder<E> {
674 InsertBuilder::new()
675 }
676
677 pub fn update<E: Entity>() -> UpdateBuilder<E> {
679 UpdateBuilder::new()
680 }
681
682 pub fn delete<E: Entity>() -> DeleteBuilder<E> {
684 DeleteBuilder::new()
685 }
686}