1use crate::dialect::{Dialect, Postgres, Sqlite};
4use crate::pagination::{Cursor, IntoCursor};
5use crate::validate::{assert_valid_sql_expression, assert_valid_sql_identifier};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum Operator {
10 Eq,
12 Ne,
14 Gt,
16 Gte,
18 Lt,
20 Lte,
22 In,
24 NotIn,
26 Regex,
28 Like,
30 ILike,
32 StartsWith,
34 EndsWith,
36 Contains,
38 Between,
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44pub enum LogicalOp {
45 And,
47 Or,
49 Not,
51}
52
53#[derive(Debug, Clone)]
55pub enum FilterExpr {
56 Simple(Filter),
58 Compound(CompoundFilter),
60}
61
62#[derive(Debug, Clone)]
64pub struct CompoundFilter {
65 pub op: LogicalOp,
66 pub filters: Vec<FilterExpr>,
67}
68
69impl CompoundFilter {
70 #[must_use]
72 pub fn and(filters: Vec<FilterExpr>) -> Self {
73 Self {
74 op: LogicalOp::And,
75 filters,
76 }
77 }
78
79 #[must_use]
81 pub fn or(filters: Vec<FilterExpr>) -> Self {
82 Self {
83 op: LogicalOp::Or,
84 filters,
85 }
86 }
87
88 #[must_use]
90 pub fn not(filter: FilterExpr) -> Self {
91 Self {
92 op: LogicalOp::Not,
93 filters: vec![filter],
94 }
95 }
96}
97
98#[derive(Debug, Clone, Copy, PartialEq, Eq)]
100pub enum AggregateFunc {
101 Count,
103 CountDistinct,
105 Sum,
107 Avg,
109 Min,
111 Max,
113}
114
115#[derive(Debug, Clone)]
117pub struct Aggregate {
118 pub func: AggregateFunc,
119 pub field: Option<String>,
121 pub alias: Option<String>,
123}
124
125impl Aggregate {
126 #[must_use]
128 pub fn count() -> Self {
129 Self {
130 func: AggregateFunc::Count,
131 field: None,
132 alias: Some("count".to_string()),
133 }
134 }
135
136 pub fn count_field(field: impl Into<String>) -> Self {
142 let field = field.into();
143 assert_valid_sql_identifier(&field, "aggregate field");
144 Self {
145 func: AggregateFunc::Count,
146 field: Some(field),
147 alias: None,
148 }
149 }
150
151 pub fn count_distinct(field: impl Into<String>) -> Self {
157 let field = field.into();
158 assert_valid_sql_identifier(&field, "aggregate field");
159 Self {
160 func: AggregateFunc::CountDistinct,
161 field: Some(field),
162 alias: None,
163 }
164 }
165
166 pub fn sum(field: impl Into<String>) -> Self {
172 let field = field.into();
173 assert_valid_sql_identifier(&field, "aggregate field");
174 Self {
175 func: AggregateFunc::Sum,
176 field: Some(field),
177 alias: None,
178 }
179 }
180
181 pub fn avg(field: impl Into<String>) -> Self {
187 let field = field.into();
188 assert_valid_sql_identifier(&field, "aggregate field");
189 Self {
190 func: AggregateFunc::Avg,
191 field: Some(field),
192 alias: None,
193 }
194 }
195
196 pub fn min(field: impl Into<String>) -> Self {
202 let field = field.into();
203 assert_valid_sql_identifier(&field, "aggregate field");
204 Self {
205 func: AggregateFunc::Min,
206 field: Some(field),
207 alias: None,
208 }
209 }
210
211 pub fn max(field: impl Into<String>) -> Self {
217 let field = field.into();
218 assert_valid_sql_identifier(&field, "aggregate field");
219 Self {
220 func: AggregateFunc::Max,
221 field: Some(field),
222 alias: None,
223 }
224 }
225
226 pub fn as_alias(mut self, alias: impl Into<String>) -> Self {
232 let alias = alias.into();
233 assert_valid_sql_identifier(&alias, "aggregate alias");
234 self.alias = Some(alias);
235 self
236 }
237
238 #[must_use]
240 pub fn to_sql(&self) -> String {
241 let expr = match (&self.func, &self.field) {
242 (AggregateFunc::Count, None) => "COUNT(*)".to_string(),
243 (AggregateFunc::Count, Some(f)) => format!("COUNT({f})"),
244 (AggregateFunc::CountDistinct, Some(f)) => format!("COUNT(DISTINCT {f})"),
245 (AggregateFunc::Sum, Some(f)) => format!("SUM({f})"),
246 (AggregateFunc::Avg, Some(f)) => format!("AVG({f})"),
247 (AggregateFunc::Min, Some(f)) => format!("MIN({f})"),
248 (AggregateFunc::Max, Some(f)) => format!("MAX({f})"),
249 _ => "COUNT(*)".to_string(),
250 };
251
252 match &self.alias {
253 Some(a) => format!("{expr} AS {a}"),
254 None => expr,
255 }
256 }
257}
258
259#[derive(Debug, Clone, PartialEq)]
261pub enum Value {
262 Null,
263 Bool(bool),
264 Int(i64),
265 Float(f64),
266 String(String),
267 Array(Vec<Value>),
268}
269
270#[derive(Debug, Clone, Copy, PartialEq, Eq)]
272pub enum SortDir {
273 Asc,
274 Desc,
275}
276
277#[derive(Debug, Clone, PartialEq, Eq)]
279pub struct SortField {
280 pub field: String,
281 pub dir: SortDir,
282}
283
284impl SortField {
285 pub fn new(field: impl Into<String>, dir: SortDir) -> Self {
287 Self {
288 field: field.into(),
289 dir,
290 }
291 }
292
293 pub fn parse_sort_string(sort: &str, allowed: &[&str]) -> Result<Vec<SortField>, String> {
303 let mut result = Vec::new();
304
305 for part in sort.split(',') {
306 let part = part.trim();
307 if part.is_empty() {
308 continue;
309 }
310
311 let (field, dir) = if let Some(stripped) = part.strip_prefix('-') {
312 (stripped, SortDir::Desc)
313 } else {
314 (part, SortDir::Asc)
315 };
316
317 if !allowed.is_empty() && !allowed.contains(&field) {
319 return Err(format!(
320 "Sort field '{field}' not allowed. Allowed: {allowed:?}"
321 ));
322 }
323
324 result.push(SortField::new(field, dir));
325 }
326
327 Ok(result)
328 }
329}
330
331#[derive(Debug, Clone)]
333pub struct Filter {
334 pub field: String,
335 pub op: Operator,
336 pub value: Value,
337}
338
339#[derive(Debug)]
341#[must_use = "QueryResult must be used to execute the query"]
342pub struct QueryResult {
343 pub sql: String,
344 pub params: Vec<Value>,
345}
346
347#[derive(Debug, Clone)]
349pub struct ComputedField {
350 pub alias: String,
352 pub expression: String,
354}
355
356impl ComputedField {
357 pub fn new(alias: impl Into<String>, expression: impl Into<String>) -> Self {
359 Self {
360 alias: alias.into(),
361 expression: expression.into(),
362 }
363 }
364
365 #[must_use]
367 pub fn to_sql(&self) -> String {
368 format!("({}) AS {}", self.expression, self.alias)
369 }
370}
371
372#[derive(Debug, Clone, Copy, PartialEq, Eq)]
374pub enum CursorDirection {
375 After,
377 Before,
379}
380
381fn build_filter_expr_impl<D: Dialect>(
387 dialect: &D,
388 expr: &FilterExpr,
389 start_idx: usize,
390) -> (String, Vec<Value>, usize) {
391 match expr {
392 FilterExpr::Simple(filter) => build_condition_impl(dialect, filter, start_idx),
393 FilterExpr::Compound(compound) => build_compound_filter_impl(dialect, compound, start_idx),
394 }
395}
396
397fn build_compound_filter_impl<D: Dialect>(
399 dialect: &D,
400 compound: &CompoundFilter,
401 start_idx: usize,
402) -> (String, Vec<Value>, usize) {
403 let mut idx = start_idx;
404 let mut all_params = Vec::new();
405 let mut conditions = Vec::new();
406
407 for filter_expr in &compound.filters {
408 let (condition, params, new_idx) = build_filter_expr_impl(dialect, filter_expr, idx);
409 conditions.push(condition);
410 all_params.extend(params);
411 idx = new_idx;
412 }
413
414 let sql = match compound.op {
415 LogicalOp::And => {
416 if conditions.len() == 1 {
417 conditions.into_iter().next().unwrap()
418 } else {
419 format!("({})", conditions.join(" AND "))
420 }
421 },
422 LogicalOp::Or => {
423 if conditions.len() == 1 {
424 conditions.into_iter().next().unwrap()
425 } else {
426 format!("({})", conditions.join(" OR "))
427 }
428 },
429 LogicalOp::Not => {
430 let inner = conditions.into_iter().next().unwrap_or_default();
431 format!("NOT ({inner})")
432 },
433 };
434
435 (sql, all_params, idx)
436}
437
438fn build_condition_impl<D: Dialect>(
440 dialect: &D,
441 filter: &Filter,
442 start_idx: usize,
443) -> (String, Vec<Value>, usize) {
444 let field = &filter.field;
445 let idx = start_idx;
446
447 match (&filter.op, &filter.value) {
448 (Operator::Eq, Value::Null) => (format!("{field} IS NULL"), vec![], idx),
450 (Operator::Ne, Value::Null) => (format!("{field} IS NOT NULL"), vec![], idx),
451
452 (Operator::In, Value::Array(values)) => {
454 let (sql, params) = dialect.in_clause(field, values, idx);
455 let new_idx = idx + params.len();
456 (sql, params, new_idx)
457 },
458 (Operator::NotIn, Value::Array(values)) => {
459 let (sql, params) = dialect.not_in_clause(field, values, idx);
460 let new_idx = idx + params.len();
461 (sql, params, new_idx)
462 },
463
464 (Operator::Eq, Value::Bool(_)) => {
466 let sql = format!("{} = {}", field, dialect.param(idx));
467 (sql, vec![filter.value.clone()], idx + 1)
468 },
469 (Operator::Ne, Value::Bool(_)) => {
470 let sql = format!("{} != {}", field, dialect.param(idx));
471 (sql, vec![filter.value.clone()], idx + 1)
472 },
473
474 (Operator::Regex, value) => {
476 let op = dialect.regex_op();
477 let sql = format!("{} {} {}", field, op, dialect.param(idx));
478 (sql, vec![value.clone()], idx + 1)
479 },
480
481 (Operator::ILike, value) => {
483 let sql = if dialect.supports_ilike() {
484 format!("{} ILIKE {}", field, dialect.param(idx))
485 } else {
486 format!("{} LIKE {}", field, dialect.param(idx))
487 };
488 (sql, vec![value.clone()], idx + 1)
489 },
490
491 (Operator::StartsWith, value) => {
493 let sql = dialect.starts_with_clause(field, idx);
494 (sql, vec![value.clone()], idx + 1)
495 },
496 (Operator::EndsWith, value) => {
497 let sql = dialect.ends_with_clause(field, idx);
498 (sql, vec![value.clone()], idx + 1)
499 },
500 (Operator::Contains, value) => {
501 let sql = dialect.contains_clause(field, idx);
502 (sql, vec![value.clone()], idx + 1)
503 },
504
505 (Operator::Between, Value::Array(values)) => {
507 if values.len() != 2 {
508 return (
510 format!("1=0 /* BETWEEN requires 2 values, got {} */", values.len()),
511 vec![],
512 idx,
513 );
514 }
515 let sql = format!(
516 "{} BETWEEN {} AND {}",
517 field,
518 dialect.param(idx),
519 dialect.param(idx + 1)
520 );
521 (sql, values.clone(), idx + 2)
522 },
523
524 (op, value) => {
526 let op_str = match op {
527 Operator::Eq => "=",
528 Operator::Ne => "!=",
529 Operator::Gt => ">",
530 Operator::Gte => ">=",
531 Operator::Lt => "<",
532 Operator::Lte => "<=",
533 Operator::Like => "LIKE",
534 _ => "=", };
536 let sql = format!("{} {} {}", field, op_str, dialect.param(idx));
537 (sql, vec![value.clone()], idx + 1)
538 },
539 }
540}
541
542#[derive(Debug)]
544pub struct QueryBuilder<D: Dialect> {
545 dialect: D,
546 table: String,
547 fields: Vec<String>,
548 computed: Vec<ComputedField>,
549 aggregates: Vec<Aggregate>,
550 filters: Vec<Filter>,
551 filter_expr: Option<FilterExpr>,
552 group_by: Vec<String>,
553 having: Option<FilterExpr>,
554 sorts: Vec<SortField>,
555 limit: Option<u32>,
556 offset: Option<u32>,
557 cursor: Option<Cursor>,
558 cursor_direction: Option<CursorDirection>,
559}
560
561impl<D: Dialect> QueryBuilder<D> {
562 pub fn new(dialect: D, table: impl Into<String>) -> Self {
568 let table = table.into();
569 assert_valid_sql_identifier(&table, "table");
570 Self {
571 dialect,
572 table,
573 fields: Vec::new(),
574 computed: Vec::new(),
575 aggregates: Vec::new(),
576 filters: Vec::new(),
577 filter_expr: None,
578 group_by: Vec::new(),
579 having: None,
580 sorts: Vec::new(),
581 limit: None,
582 offset: None,
583 cursor: None,
584 cursor_direction: None,
585 }
586 }
587
588 pub fn fields(mut self, fields: &[&str]) -> Self {
594 for field in fields {
595 assert_valid_sql_identifier(field, "field");
596 }
597 self.fields = fields.iter().map(|s| (*s).to_string()).collect();
598 self
599 }
600
601 pub fn computed(mut self, alias: impl Into<String>, expression: impl Into<String>) -> Self {
618 let alias = alias.into();
619 let expression = expression.into();
620 assert_valid_sql_identifier(&alias, "computed field alias");
621 assert_valid_sql_expression(&expression, "computed field");
622 self.computed.push(ComputedField::new(alias, expression));
623 self
624 }
625
626 pub fn aggregate(mut self, agg: Aggregate) -> Self {
628 self.aggregates.push(agg);
629 self
630 }
631
632 pub fn count(mut self) -> Self {
634 self.aggregates.push(Aggregate::count());
635 self
636 }
637
638 pub fn sum(mut self, field: impl Into<String>) -> Self {
640 self.aggregates.push(Aggregate::sum(field));
641 self
642 }
643
644 pub fn avg(mut self, field: impl Into<String>) -> Self {
646 self.aggregates.push(Aggregate::avg(field));
647 self
648 }
649
650 pub fn min(mut self, field: impl Into<String>) -> Self {
652 self.aggregates.push(Aggregate::min(field));
653 self
654 }
655
656 pub fn max(mut self, field: impl Into<String>) -> Self {
658 self.aggregates.push(Aggregate::max(field));
659 self
660 }
661
662 pub fn filter(mut self, field: impl Into<String>, op: Operator, value: Value) -> Self {
668 let field = field.into();
669 assert_valid_sql_identifier(&field, "filter field");
670 self.filters.push(Filter { field, op, value });
671 self
672 }
673
674 pub fn filter_expr(mut self, expr: FilterExpr) -> Self {
676 self.filter_expr = Some(expr);
677 self
678 }
679
680 pub fn and(mut self, filters: Vec<FilterExpr>) -> Self {
682 self.filter_expr = Some(FilterExpr::Compound(CompoundFilter::and(filters)));
683 self
684 }
685
686 pub fn or(mut self, filters: Vec<FilterExpr>) -> Self {
688 self.filter_expr = Some(FilterExpr::Compound(CompoundFilter::or(filters)));
689 self
690 }
691
692 pub fn group_by(mut self, fields: &[&str]) -> Self {
698 for field in fields {
699 assert_valid_sql_identifier(field, "group by field");
700 }
701 self.group_by = fields.iter().map(|s| (*s).to_string()).collect();
702 self
703 }
704
705 pub fn having(mut self, expr: FilterExpr) -> Self {
707 self.having = Some(expr);
708 self
709 }
710
711 pub fn sort(mut self, field: impl Into<String>, dir: SortDir) -> Self {
717 let field = field.into();
718 assert_valid_sql_identifier(&field, "sort field");
719 self.sorts.push(SortField::new(field, dir));
720 self
721 }
722
723 pub fn sorts(mut self, sorts: &[SortField]) -> Self {
725 self.sorts.extend(sorts.iter().cloned());
726 self
727 }
728
729 pub fn page(mut self, page: u32, limit: u32) -> Self {
731 self.limit = Some(limit);
732 self.offset = Some(page.saturating_sub(1).saturating_mul(limit));
733 self
734 }
735
736 pub fn limit_offset(mut self, limit: u32, offset: u32) -> Self {
738 self.limit = Some(limit);
739 self.offset = Some(offset);
740 self
741 }
742
743 pub fn limit(mut self, limit: u32) -> Self {
745 self.limit = Some(limit);
746 self
747 }
748
749 pub fn after_cursor(mut self, cursor: impl IntoCursor) -> Self {
759 if let Some(c) = cursor.into_cursor() {
760 self.cursor = Some(c);
761 self.cursor_direction = Some(CursorDirection::After);
762 }
763 self
764 }
765
766 pub fn before_cursor(mut self, cursor: impl IntoCursor) -> Self {
775 if let Some(c) = cursor.into_cursor() {
776 self.cursor = Some(c);
777 self.cursor_direction = Some(CursorDirection::Before);
778 }
779 self
780 }
781
782 pub fn build(self) -> QueryResult {
784 let mut sql = String::new();
785 let mut params = Vec::new();
786 let mut param_idx = 1usize;
787
788 let mut select_parts = Vec::new();
790
791 if !self.fields.is_empty() {
793 select_parts.extend(self.fields.clone());
794 }
795
796 for comp in &self.computed {
798 select_parts.push(comp.to_sql());
799 }
800
801 for agg in &self.aggregates {
803 select_parts.push(agg.to_sql());
804 }
805
806 let select_str = if select_parts.is_empty() {
807 "*".to_string()
808 } else {
809 select_parts.join(", ")
810 };
811
812 sql.push_str(&format!("SELECT {} FROM {}", select_str, self.table));
813
814 let has_filter_expr = self.filter_expr.is_some();
816 let has_simple_filters = !self.filters.is_empty();
817 let has_cursor = self.cursor.is_some() && self.cursor_direction.is_some();
818
819 if has_filter_expr || has_simple_filters || has_cursor {
820 sql.push_str(" WHERE ");
821 let mut all_conditions = Vec::new();
822
823 if let Some(ref expr) = self.filter_expr {
825 let (condition, new_params, new_idx) =
826 build_filter_expr_impl(&self.dialect, expr, param_idx);
827 all_conditions.push(condition);
828 params.extend(new_params);
829 param_idx = new_idx;
830 }
831
832 for filter in &self.filters {
834 let (condition, new_params, new_idx) =
835 build_condition_impl(&self.dialect, filter, param_idx);
836 all_conditions.push(condition);
837 params.extend(new_params);
838 param_idx = new_idx;
839 }
840
841 if let (Some(cursor), Some(direction)) = (&self.cursor, self.cursor_direction) {
843 let (condition, new_params, new_idx) =
844 self.build_cursor_condition(cursor, direction, param_idx);
845 if !condition.is_empty() {
846 all_conditions.push(condition);
847 params.extend(new_params);
848 param_idx = new_idx;
849 }
850 }
851
852 sql.push_str(&all_conditions.join(" AND "));
853 }
854
855 if !self.group_by.is_empty() {
857 sql.push_str(&format!(" GROUP BY {}", self.group_by.join(", ")));
858 }
859
860 if let Some(ref expr) = self.having {
863 let (condition, new_params, _new_idx) =
864 build_filter_expr_impl(&self.dialect, expr, param_idx);
865 sql.push_str(&format!(" HAVING {condition}"));
866 params.extend(new_params);
867 }
868
869 if !self.sorts.is_empty() {
871 sql.push_str(" ORDER BY ");
872 let sort_parts: Vec<String> = self
873 .sorts
874 .iter()
875 .map(|s| {
876 let dir = match s.dir {
877 SortDir::Asc => "ASC",
878 SortDir::Desc => "DESC",
879 };
880 format!("{} {}", s.field, dir)
881 })
882 .collect();
883 sql.push_str(&sort_parts.join(", "));
884 }
885
886 if let Some(limit) = self.limit {
888 sql.push_str(&format!(" LIMIT {limit}"));
889 }
890 if let Some(offset) = self.offset {
891 sql.push_str(&format!(" OFFSET {offset}"));
892 }
893
894 QueryResult { sql, params }
895 }
896
897 fn build_cursor_condition(
903 &self,
904 cursor: &Cursor,
905 direction: CursorDirection,
906 start_idx: usize,
907 ) -> (String, Vec<Value>, usize) {
908 let sort_fields: Vec<SortField> = if self.sorts.is_empty() {
910 cursor
911 .fields
912 .iter()
913 .map(|(name, _)| SortField::new(name.clone(), SortDir::Asc))
914 .collect()
915 } else {
916 self.sorts.clone()
917 };
918
919 if sort_fields.is_empty() {
920 return (String::new(), vec![], start_idx);
921 }
922
923 let mut cursor_values: Vec<(&str, &Value)> = Vec::new();
925 for sort in &sort_fields {
926 if let Some((_, value)) = cursor.fields.iter().find(|(name, _)| name == &sort.field) {
927 cursor_values.push((&sort.field, value));
928 }
929 }
930
931 if cursor_values.is_empty() {
932 return (String::new(), vec![], start_idx);
933 }
934
935 let mut idx = start_idx;
936 let mut params = Vec::new();
937
938 if cursor_values.len() == 1 {
939 let (field, value) = cursor_values[0];
941 let sort = &sort_fields[0];
942 let op = match (direction, sort.dir) {
943 (CursorDirection::After, SortDir::Asc) => ">",
944 (CursorDirection::After, SortDir::Desc) => "<",
945 (CursorDirection::Before, SortDir::Asc) => "<",
946 (CursorDirection::Before, SortDir::Desc) => ">",
947 };
948
949 let sql = format!("{} {} {}", field, op, self.dialect.param(idx));
950 params.push(value.clone());
951 idx += 1;
952
953 (sql, params, idx)
954 } else {
955 let fields: Vec<&str> = cursor_values.iter().map(|(f, _)| *f).collect();
958 let placeholders: Vec<String> = cursor_values
959 .iter()
960 .enumerate()
961 .map(|(i, (_, value))| {
962 params.push((*value).clone());
963 self.dialect.param(idx + i)
964 })
965 .collect();
966 idx += cursor_values.len();
967
968 let primary_dir = sort_fields[0].dir;
970 let op = match (direction, primary_dir) {
971 (CursorDirection::After, SortDir::Asc) => ">",
972 (CursorDirection::After, SortDir::Desc) => "<",
973 (CursorDirection::Before, SortDir::Asc) => "<",
974 (CursorDirection::Before, SortDir::Desc) => ">",
975 };
976
977 let sql = format!(
978 "({}) {} ({})",
979 fields.join(", "),
980 op,
981 placeholders.join(", ")
982 );
983
984 (sql, params, idx)
985 }
986 }
987}
988
989pub fn simple(field: impl Into<String>, op: Operator, value: Value) -> FilterExpr {
995 let field = field.into();
996 assert_valid_sql_identifier(&field, "filter field");
997 FilterExpr::Simple(Filter { field, op, value })
998}
999
1000#[must_use]
1002pub fn and(filters: Vec<FilterExpr>) -> FilterExpr {
1003 FilterExpr::Compound(CompoundFilter::and(filters))
1004}
1005
1006#[must_use]
1008pub fn or(filters: Vec<FilterExpr>) -> FilterExpr {
1009 FilterExpr::Compound(CompoundFilter::or(filters))
1010}
1011
1012#[must_use]
1014pub fn not(filter: FilterExpr) -> FilterExpr {
1015 FilterExpr::Compound(CompoundFilter::not(filter))
1016}
1017
1018#[derive(Debug)]
1024pub struct InsertBuilder<D: Dialect> {
1025 dialect: D,
1026 table: String,
1027 columns: Vec<String>,
1028 values: Vec<Vec<Value>>,
1029 returning: Vec<String>,
1030}
1031
1032impl<D: Dialect> InsertBuilder<D> {
1033 pub fn new(dialect: D, table: impl Into<String>) -> Self {
1039 let table = table.into();
1040 assert_valid_sql_identifier(&table, "table");
1041 Self {
1042 dialect,
1043 table,
1044 columns: Vec::new(),
1045 values: Vec::new(),
1046 returning: Vec::new(),
1047 }
1048 }
1049
1050 pub fn columns(mut self, columns: &[&str]) -> Self {
1056 for col in columns {
1057 assert_valid_sql_identifier(col, "column");
1058 }
1059 self.columns = columns.iter().map(|s| (*s).to_string()).collect();
1060 self
1061 }
1062
1063 pub fn values(mut self, values: Vec<Value>) -> Self {
1065 self.values.push(values);
1066 self
1067 }
1068
1069 pub fn values_many(mut self, rows: Vec<Vec<Value>>) -> Self {
1071 self.values.extend(rows);
1072 self
1073 }
1074
1075 pub fn returning(mut self, columns: &[&str]) -> Self {
1081 for col in columns {
1082 assert_valid_sql_identifier(col, "returning column");
1083 }
1084 self.returning = columns.iter().map(|s| (*s).to_string()).collect();
1085 self
1086 }
1087
1088 pub fn build(self) -> QueryResult {
1090 let mut sql = String::new();
1091 let mut params = Vec::new();
1092 let mut param_idx = 1usize;
1093
1094 sql.push_str(&format!(
1096 "INSERT INTO {} ({})",
1097 self.table,
1098 self.columns.join(", ")
1099 ));
1100
1101 let mut value_groups = Vec::new();
1103 for row in &self.values {
1104 let placeholders: Vec<String> = row
1105 .iter()
1106 .map(|v| {
1107 let p = self.dialect.param(param_idx);
1108 params.push(v.clone());
1109 param_idx += 1;
1110 p
1111 })
1112 .collect();
1113 value_groups.push(format!("({})", placeholders.join(", ")));
1114 }
1115 sql.push_str(&format!(" VALUES {}", value_groups.join(", ")));
1116
1117 if !self.returning.is_empty() {
1119 sql.push_str(&format!(" RETURNING {}", self.returning.join(", ")));
1120 }
1121
1122 QueryResult { sql, params }
1123 }
1124}
1125
1126#[derive(Debug)]
1132pub struct UpdateBuilder<D: Dialect> {
1133 dialect: D,
1134 table: String,
1135 sets: Vec<(String, Value)>,
1136 filters: Vec<Filter>,
1137 filter_expr: Option<FilterExpr>,
1138 returning: Vec<String>,
1139}
1140
1141impl<D: Dialect> UpdateBuilder<D> {
1142 pub fn new(dialect: D, table: impl Into<String>) -> Self {
1148 let table = table.into();
1149 assert_valid_sql_identifier(&table, "table");
1150 Self {
1151 dialect,
1152 table,
1153 sets: Vec::new(),
1154 filters: Vec::new(),
1155 filter_expr: None,
1156 returning: Vec::new(),
1157 }
1158 }
1159
1160 pub fn set(mut self, column: impl Into<String>, value: Value) -> Self {
1166 let column = column.into();
1167 assert_valid_sql_identifier(&column, "column");
1168 self.sets.push((column, value));
1169 self
1170 }
1171
1172 pub fn set_many(mut self, pairs: Vec<(&str, Value)>) -> Self {
1178 for (col, val) in pairs {
1179 assert_valid_sql_identifier(col, "column");
1180 self.sets.push((col.to_string(), val));
1181 }
1182 self
1183 }
1184
1185 pub fn filter(mut self, field: impl Into<String>, op: Operator, value: Value) -> Self {
1191 let field = field.into();
1192 assert_valid_sql_identifier(&field, "filter field");
1193 self.filters.push(Filter { field, op, value });
1194 self
1195 }
1196
1197 pub fn filter_expr(mut self, expr: FilterExpr) -> Self {
1200 self.filter_expr = Some(expr);
1201 self
1202 }
1203
1204 pub fn returning(mut self, columns: &[&str]) -> Self {
1210 for col in columns {
1211 assert_valid_sql_identifier(col, "returning column");
1212 }
1213 self.returning = columns.iter().map(|s| (*s).to_string()).collect();
1214 self
1215 }
1216
1217 pub fn build(self) -> QueryResult {
1219 let mut sql = String::new();
1220 let mut params = Vec::new();
1221 let mut param_idx = 1usize;
1222
1223 sql.push_str(&format!("UPDATE {} SET ", self.table));
1225
1226 let set_parts: Vec<String> = self
1227 .sets
1228 .iter()
1229 .map(|(col, val)| {
1230 let p = self.dialect.param(param_idx);
1231 params.push(val.clone());
1232 param_idx += 1;
1233 format!("{col} = {p}")
1234 })
1235 .collect();
1236 sql.push_str(&set_parts.join(", "));
1237
1238 let has_filter_expr = self.filter_expr.is_some();
1240 let has_simple_filters = !self.filters.is_empty();
1241
1242 if has_filter_expr || has_simple_filters {
1243 sql.push_str(" WHERE ");
1244 let mut all_conditions = Vec::new();
1245
1246 if let Some(ref expr) = self.filter_expr {
1247 let (condition, new_params, new_idx) =
1248 build_filter_expr_impl(&self.dialect, expr, param_idx);
1249 all_conditions.push(condition);
1250 params.extend(new_params);
1251 param_idx = new_idx;
1252 }
1253
1254 for filter in &self.filters {
1255 let (condition, new_params, new_idx) =
1256 build_condition_impl(&self.dialect, filter, param_idx);
1257 all_conditions.push(condition);
1258 params.extend(new_params);
1259 param_idx = new_idx;
1260 }
1261
1262 sql.push_str(&all_conditions.join(" AND "));
1263 }
1264
1265 if !self.returning.is_empty() {
1267 sql.push_str(&format!(" RETURNING {}", self.returning.join(", ")));
1268 }
1269
1270 QueryResult { sql, params }
1271 }
1272}
1273
1274#[derive(Debug)]
1280pub struct DeleteBuilder<D: Dialect> {
1281 dialect: D,
1282 table: String,
1283 filters: Vec<Filter>,
1284 filter_expr: Option<FilterExpr>,
1285 returning: Vec<String>,
1286}
1287
1288impl<D: Dialect> DeleteBuilder<D> {
1289 pub fn new(dialect: D, table: impl Into<String>) -> Self {
1295 let table = table.into();
1296 assert_valid_sql_identifier(&table, "table");
1297 Self {
1298 dialect,
1299 table,
1300 filters: Vec::new(),
1301 filter_expr: None,
1302 returning: Vec::new(),
1303 }
1304 }
1305
1306 pub fn filter(mut self, field: impl Into<String>, op: Operator, value: Value) -> Self {
1312 let field = field.into();
1313 assert_valid_sql_identifier(&field, "filter field");
1314 self.filters.push(Filter { field, op, value });
1315 self
1316 }
1317
1318 pub fn filter_expr(mut self, expr: FilterExpr) -> Self {
1321 self.filter_expr = Some(expr);
1322 self
1323 }
1324
1325 pub fn returning(mut self, columns: &[&str]) -> Self {
1331 for col in columns {
1332 assert_valid_sql_identifier(col, "returning column");
1333 }
1334 self.returning = columns.iter().map(|s| (*s).to_string()).collect();
1335 self
1336 }
1337
1338 pub fn build(self) -> QueryResult {
1340 let mut sql = String::new();
1341 let mut params = Vec::new();
1342 let mut param_idx = 1usize;
1343
1344 sql.push_str(&format!("DELETE FROM {}", self.table));
1346
1347 let has_filter_expr = self.filter_expr.is_some();
1349 let has_simple_filters = !self.filters.is_empty();
1350
1351 if has_filter_expr || has_simple_filters {
1352 sql.push_str(" WHERE ");
1353 let mut all_conditions = Vec::new();
1354
1355 if let Some(ref expr) = self.filter_expr {
1356 let (condition, new_params, new_idx) =
1357 build_filter_expr_impl(&self.dialect, expr, param_idx);
1358 all_conditions.push(condition);
1359 params.extend(new_params);
1360 param_idx = new_idx;
1361 }
1362
1363 for filter in &self.filters {
1364 let (condition, new_params, new_idx) =
1365 build_condition_impl(&self.dialect, filter, param_idx);
1366 all_conditions.push(condition);
1367 params.extend(new_params);
1368 param_idx = new_idx;
1369 }
1370
1371 sql.push_str(&all_conditions.join(" AND "));
1372 }
1373
1374 if !self.returning.is_empty() {
1376 sql.push_str(&format!(" RETURNING {}", self.returning.join(", ")));
1377 }
1378
1379 QueryResult { sql, params }
1380 }
1381}
1382
1383pub fn insert(table: impl Into<String>) -> InsertBuilder<Postgres> {
1389 InsertBuilder::new(Postgres, table)
1390}
1391
1392pub fn update(table: impl Into<String>) -> UpdateBuilder<Postgres> {
1394 UpdateBuilder::new(Postgres, table)
1395}
1396
1397pub fn delete(table: impl Into<String>) -> DeleteBuilder<Postgres> {
1399 DeleteBuilder::new(Postgres, table)
1400}
1401
1402pub fn insert_sqlite(table: impl Into<String>) -> InsertBuilder<Sqlite> {
1404 InsertBuilder::new(Sqlite, table)
1405}
1406
1407pub fn update_sqlite(table: impl Into<String>) -> UpdateBuilder<Sqlite> {
1409 UpdateBuilder::new(Sqlite, table)
1410}
1411
1412pub fn delete_sqlite(table: impl Into<String>) -> DeleteBuilder<Sqlite> {
1414 DeleteBuilder::new(Sqlite, table)
1415}