halo_space/
condition.rs

1//! Condition chaining and builders.
2use crate::DeleteBuilder;
3use crate::cond::Cond;
4use crate::flavor::{Flavor, default_flavor};
5use crate::modifiers::{Arg, Builder};
6use crate::select::{JoinOption, SelectBuilder};
7use crate::update::UpdateBuilder;
8use crate::value::SqlValue;
9use crate::where_clause::{WhereClause, WhereClauseRef};
10use std::collections::HashMap;
11use std::sync::Arc;
12
13/// Condition operators.
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum Operator {
16    Equal,
17    NotEqual,
18    IsNull,
19    IsNotNull,
20    GreaterThan,
21    LessThan,
22    GreaterEqualThan,
23    LessEqualThan,
24    In,
25    NotIn,
26    Like,
27    NotLike,
28    Limit,
29    Offset,
30    Between,
31    NotBetween,
32    OrderBy,
33    OrderByDesc,
34    OrderByAsc,
35    GroupBy,
36    Join,
37}
38
39/// Condition value supporting scalar or list.
40#[derive(Debug, Clone)]
41pub enum ConditionValue {
42    Single(Arg),
43    List(Vec<Arg>),
44}
45
46impl ConditionValue {
47    pub fn to_vec(&self) -> Vec<Arg> {
48        match self {
49            Self::Single(v) => vec![v.clone()],
50            Self::List(v) => v.clone(),
51        }
52    }
53
54    pub fn first(&self) -> Option<Arg> {
55        match self {
56            Self::Single(v) => Some(v.clone()),
57            Self::List(v) => v.first().cloned(),
58        }
59    }
60
61    pub fn pair(&self) -> Option<(Arg, Arg)> {
62        match self {
63            Self::Single(_) => None,
64            Self::List(v) if v.len() >= 2 => Some((v[0].clone(), v[1].clone())),
65            _ => None,
66        }
67    }
68
69    pub fn is_empty(&self) -> bool {
70        matches!(self, Self::List(v) if v.is_empty())
71    }
72}
73
74impl Default for ConditionValue {
75    fn default() -> Self {
76        Self::Single(SqlValue::Null.into())
77    }
78}
79
80impl<T: Into<Arg>> From<T> for ConditionValue {
81    fn from(v: T) -> Self {
82        Self::Single(v.into())
83    }
84}
85
86impl<T: Into<Arg>> From<Vec<T>> for ConditionValue {
87    fn from(v: Vec<T>) -> Self {
88        Self::List(v.into_iter().map(Into::into).collect())
89    }
90}
91
92impl<T: Into<Arg>, const N: usize> From<[T; N]> for ConditionValue {
93    fn from(v: [T; N]) -> Self {
94        Self::List(v.into_iter().map(Into::into).collect())
95    }
96}
97
98impl<T: Into<Arg>> From<HashMap<String, T>> for ConditionValue {
99    fn from(v: HashMap<String, T>) -> Self {
100        Self::List(v.into_values().map(Into::into).collect())
101    }
102}
103
104/// Options to control skip/value functions.
105#[derive(Clone, Default)]
106pub struct ChainOptions {
107    pub skip: bool,
108    pub skip_fn: Option<Arc<dyn Fn() -> bool + Send + Sync>>,
109    pub value_fn: Option<Arc<dyn Fn() -> ConditionValue + Send + Sync>>,
110    pub or_values_fn: Option<Arc<dyn Fn() -> Vec<ConditionValue> + Send + Sync>>,
111}
112
113impl ChainOptions {
114    pub fn skip(mut self, skip: bool) -> Self {
115        self.skip = skip;
116        self
117    }
118
119    pub fn skip_fn(mut self, f: impl Fn() -> bool + Send + Sync + 'static) -> Self {
120        self.skip_fn = Some(Arc::new(f));
121        self
122    }
123
124    pub fn value_fn(mut self, f: impl Fn() -> ConditionValue + Send + Sync + 'static) -> Self {
125        self.value_fn = Some(Arc::new(f));
126        self
127    }
128
129    pub fn or_values_fn(
130        mut self,
131        f: impl Fn() -> Vec<ConditionValue> + Send + Sync + 'static,
132    ) -> Self {
133        self.or_values_fn = Some(Arc::new(f));
134        self
135    }
136}
137
138impl std::fmt::Debug for ChainOptions {
139    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
140        f.debug_struct("ChainOptions")
141            .field("skip", &self.skip)
142            .field("has_skip_fn", &self.skip_fn.is_some())
143            .field("has_value_fn", &self.value_fn.is_some())
144            .field("has_or_values_fn", &self.or_values_fn.is_some())
145            .finish()
146    }
147}
148
149/// Join condition metadata.
150#[derive(Debug, Clone)]
151pub struct JoinCondition {
152    pub option: Option<JoinOption>,
153    pub table: String,
154    pub on_expr: Vec<String>,
155}
156
157/// Single condition item.
158#[derive(Clone)]
159pub struct Condition {
160    pub skip: bool,
161    pub skip_fn: Option<Arc<dyn Fn() -> bool + Send + Sync>>,
162    pub or: bool,
163    pub or_operators: Vec<Operator>,
164    pub or_fields: Vec<String>,
165    pub or_values: Vec<ConditionValue>,
166    pub or_values_fn: Option<Arc<dyn Fn() -> Vec<ConditionValue> + Send + Sync>>,
167    pub field: String,
168    pub operator: Operator,
169    pub value: ConditionValue,
170    pub value_fn: Option<Arc<dyn Fn() -> ConditionValue + Send + Sync>>,
171    pub join: Option<JoinCondition>,
172    pub where_clause: Option<WhereClauseRef>,
173}
174
175impl Condition {
176    pub fn new(
177        field: impl Into<String>,
178        operator: Operator,
179        value: impl Into<ConditionValue>,
180    ) -> Self {
181        Self {
182            skip: false,
183            skip_fn: None,
184            or: false,
185            or_operators: Vec::new(),
186            or_fields: Vec::new(),
187            or_values: Vec::new(),
188            or_values_fn: None,
189            field: field.into(),
190            operator,
191            value: value.into(),
192            value_fn: None,
193            join: None,
194            where_clause: None,
195        }
196    }
197}
198
199impl std::fmt::Debug for Condition {
200    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
201        f.debug_struct("Condition")
202            .field("skip", &self.skip)
203            .field("or", &self.or)
204            .field("field", &self.field)
205            .field("operator", &self.operator)
206            .field("value", &self.value)
207            .field("or_fields", &self.or_fields)
208            .field("or_operators", &self.or_operators)
209            .field("join", &self.join)
210            .field("has_skip_fn", &self.skip_fn.is_some())
211            .field("has_value_fn", &self.value_fn.is_some())
212            .field("has_or_values_fn", &self.or_values_fn.is_some())
213            .field("has_where_clause", &self.where_clause.is_some())
214            .finish()
215    }
216}
217
218/// 条件链。
219#[derive(Debug, Default, Clone)]
220pub struct Chain {
221    conditions: Vec<Condition>,
222}
223
224impl Chain {
225    pub fn new() -> Self {
226        Self::default()
227    }
228
229    /// Modify the last condition (no-op if absent); simulates Go-style variadic options that adjust the tail.
230    fn map_last(mut self, f: impl FnOnce(&mut Condition)) -> Self {
231        if let Some(last) = self.conditions.last_mut() {
232            f(last);
233        }
234        self
235    }
236
237    pub fn add_condition(mut self, condition: Condition) -> Self {
238        self.conditions.push(condition);
239        self
240    }
241
242    fn add_chain(
243        mut self,
244        field: impl Into<String>,
245        operator: Operator,
246        value: impl Into<ConditionValue>,
247        opts: ChainOptions,
248    ) -> Self {
249        self.conditions.push(Condition {
250            skip: opts.skip,
251            skip_fn: opts.skip_fn,
252            or: false,
253            or_operators: Vec::new(),
254            or_fields: Vec::new(),
255            or_values: Vec::new(),
256            or_values_fn: None,
257            field: field.into(),
258            operator,
259            value: value.into(),
260            value_fn: opts.value_fn,
261            join: None,
262            where_clause: None,
263        });
264        self
265    }
266
267    pub fn equal(self, field: impl Into<String>, value: impl Into<ConditionValue>) -> Self {
268        self.add_chain(field, Operator::Equal, value, ChainOptions::default())
269    }
270
271    /// Set value_fn on the last condition (higher priority than explicit value), similar to Go's WithValueFunc.
272    pub fn value_fn(self, f: impl Fn() -> ConditionValue + Send + Sync + 'static) -> Self {
273        self.map_last(|c| c.value_fn = Some(Arc::new(f)))
274    }
275
276    /// Set skip on the last condition, similar to Go's WithSkip.
277    pub fn skip(self, skip: bool) -> Self {
278        self.map_last(|c| c.skip = skip)
279    }
280
281    /// Set skip_fn on the last condition (higher priority than skip), similar to Go's WithSkipFunc.
282    pub fn skip_fn(self, f: impl Fn() -> bool + Send + Sync + 'static) -> Self {
283        self.map_last(|c| c.skip_fn = Some(Arc::new(f)))
284    }
285
286    pub fn equal_opts(
287        self,
288        field: impl Into<String>,
289        value: impl Into<ConditionValue>,
290        opts: ChainOptions,
291    ) -> Self {
292        self.add_chain(field, Operator::Equal, value, opts)
293    }
294
295    pub fn not_equal(self, field: impl Into<String>, value: impl Into<ConditionValue>) -> Self {
296        self.add_chain(field, Operator::NotEqual, value, ChainOptions::default())
297    }
298
299    pub fn is_null(self, field: impl Into<String>) -> Self {
300        self.add_chain(
301            field,
302            Operator::IsNull,
303            ConditionValue::default(),
304            ChainOptions::default(),
305        )
306    }
307
308    pub fn is_not_null(self, field: impl Into<String>) -> Self {
309        self.add_chain(
310            field,
311            Operator::IsNotNull,
312            ConditionValue::default(),
313            ChainOptions::default(),
314        )
315    }
316
317    pub fn greater_than(self, field: impl Into<String>, value: impl Into<ConditionValue>) -> Self {
318        self.add_chain(field, Operator::GreaterThan, value, ChainOptions::default())
319    }
320
321    pub fn less_than(self, field: impl Into<String>, value: impl Into<ConditionValue>) -> Self {
322        self.add_chain(field, Operator::LessThan, value, ChainOptions::default())
323    }
324
325    pub fn greater_equal_than(
326        self,
327        field: impl Into<String>,
328        value: impl Into<ConditionValue>,
329    ) -> Self {
330        self.add_chain(
331            field,
332            Operator::GreaterEqualThan,
333            value,
334            ChainOptions::default(),
335        )
336    }
337
338    pub fn less_equal_than(
339        self,
340        field: impl Into<String>,
341        value: impl Into<ConditionValue>,
342    ) -> Self {
343        self.add_chain(
344            field,
345            Operator::LessEqualThan,
346            value,
347            ChainOptions::default(),
348        )
349    }
350
351    pub fn like(self, field: impl Into<String>, value: impl Into<ConditionValue>) -> Self {
352        self.add_chain(field, Operator::Like, value, ChainOptions::default())
353    }
354
355    pub fn not_like(self, field: impl Into<String>, value: impl Into<ConditionValue>) -> Self {
356        self.add_chain(field, Operator::NotLike, value, ChainOptions::default())
357    }
358
359    pub fn between(self, field: impl Into<String>, value: impl Into<ConditionValue>) -> Self {
360        self.add_chain(field, Operator::Between, value, ChainOptions::default())
361    }
362
363    pub fn in_(self, field: impl Into<String>, value: impl Into<ConditionValue>) -> Self {
364        self.add_chain(field, Operator::In, value, ChainOptions::default())
365    }
366
367    pub fn not_in(self, field: impl Into<String>, value: impl Into<ConditionValue>) -> Self {
368        self.add_chain(field, Operator::NotIn, value, ChainOptions::default())
369    }
370
371    pub fn or(
372        mut self,
373        fields: impl IntoIterator<Item = impl Into<String>>,
374        operators: impl IntoIterator<Item = Operator>,
375        values: impl IntoIterator<Item = impl Into<ConditionValue>>,
376        opts: ChainOptions,
377    ) -> Self {
378        let mut cond = Condition {
379            skip: opts.skip,
380            skip_fn: opts.skip_fn,
381            or: true,
382            or_operators: operators.into_iter().collect(),
383            or_fields: fields.into_iter().map(Into::into).collect(),
384            or_values: values.into_iter().map(Into::into).collect(),
385            or_values_fn: opts.or_values_fn,
386            field: String::new(),
387            operator: Operator::Equal,
388            value: ConditionValue::default(),
389            value_fn: None,
390            join: None,
391            where_clause: None,
392        };
393
394        if let Some(f) = opts.value_fn {
395            cond.value_fn = Some(f);
396        }
397
398        self.conditions.push(cond);
399        self
400    }
401
402    pub fn order_by(self, value: impl Into<ConditionValue>) -> Self {
403        self.add_chain("", Operator::OrderBy, value, ChainOptions::default())
404    }
405
406    pub fn order_by_desc(self, field: impl Into<String>) -> Self {
407        self.add_chain(
408            field,
409            Operator::OrderByDesc,
410            ConditionValue::default(),
411            ChainOptions::default(),
412        )
413    }
414
415    pub fn order_by_asc(self, field: impl Into<String>) -> Self {
416        self.add_chain(
417            field,
418            Operator::OrderByAsc,
419            ConditionValue::default(),
420            ChainOptions::default(),
421        )
422    }
423
424    pub fn limit(self, value: impl Into<ConditionValue>) -> Self {
425        self.add_chain("", Operator::Limit, value, ChainOptions::default())
426    }
427
428    pub fn offset(self, value: impl Into<ConditionValue>) -> Self {
429        self.add_chain("", Operator::Offset, value, ChainOptions::default())
430    }
431
432    pub fn page(self, page: i64, page_size: i64) -> Self {
433        let offset = (page - 1) * page_size;
434        self.offset(offset).limit(page_size)
435    }
436
437    pub fn group_by(self, field: impl Into<String>) -> Self {
438        self.add_chain(
439            field,
440            Operator::GroupBy,
441            ConditionValue::default(),
442            ChainOptions::default(),
443        )
444    }
445
446    pub fn join(
447        mut self,
448        option: JoinOption,
449        table: impl Into<String>,
450        on_expr: impl IntoIterator<Item = impl Into<String>>,
451    ) -> Self {
452        self.conditions.push(Condition {
453            skip: false,
454            skip_fn: None,
455            or: false,
456            or_operators: Vec::new(),
457            or_fields: Vec::new(),
458            or_values: Vec::new(),
459            or_values_fn: None,
460            field: String::new(),
461            operator: Operator::Join,
462            value: ConditionValue::default(),
463            value_fn: None,
464            join: Some(JoinCondition {
465                option: Some(option),
466                table: table.into(),
467                on_expr: on_expr.into_iter().map(Into::into).collect(),
468            }),
469            where_clause: None,
470        });
471        self
472    }
473
474    pub fn where_clause(mut self, wc: WhereClauseRef) -> Self {
475        self.conditions.push(Condition {
476            skip: false,
477            skip_fn: None,
478            or: false,
479            or_operators: Vec::new(),
480            or_fields: Vec::new(),
481            or_values: Vec::new(),
482            or_values_fn: None,
483            field: String::new(),
484            operator: Operator::Equal,
485            value: ConditionValue::default(),
486            value_fn: None,
487            join: None,
488            where_clause: Some(wc),
489        });
490        self
491    }
492
493    pub fn build(self) -> Vec<Condition> {
494        self.conditions
495    }
496}
497
498/// UpdateField operator kinds.
499#[derive(Debug, Clone, Copy, PartialEq, Eq)]
500pub enum UpdateFieldOperator {
501    Incr,
502    Decr,
503    Assign,
504    Add,
505    Sub,
506    Mul,
507    Div,
508}
509
510/// Options for UpdateField.
511#[derive(Clone, Default)]
512pub struct UpdateFieldOptions {
513    pub skip: bool,
514    pub skip_fn: Option<Arc<dyn Fn() -> bool + Send + Sync>>,
515    pub value_fn: Option<Arc<dyn Fn() -> Arg + Send + Sync>>,
516}
517
518impl UpdateFieldOptions {
519    pub fn skip(mut self, skip: bool) -> Self {
520        self.skip = skip;
521        self
522    }
523
524    pub fn skip_fn(mut self, f: impl Fn() -> bool + Send + Sync + 'static) -> Self {
525        self.skip_fn = Some(Arc::new(f));
526        self
527    }
528
529    pub fn value_fn(mut self, f: impl Fn() -> Arg + Send + Sync + 'static) -> Self {
530        self.value_fn = Some(Arc::new(f));
531        self
532    }
533}
534
535impl std::fmt::Debug for UpdateFieldOptions {
536    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
537        f.debug_struct("UpdateFieldOptions")
538            .field("skip", &self.skip)
539            .field("has_skip_fn", &self.skip_fn.is_some())
540            .field("has_value_fn", &self.value_fn.is_some())
541            .finish()
542    }
543}
544
545/// UpdateField descriptor.
546#[derive(Clone)]
547pub struct UpdateField {
548    pub skip: bool,
549    pub skip_fn: Option<Arc<dyn Fn() -> bool + Send + Sync>>,
550    pub field: String,
551    pub operator: UpdateFieldOperator,
552    pub value: Option<Arg>,
553    pub value_fn: Option<Arc<dyn Fn() -> Arg + Send + Sync>>,
554}
555
556impl UpdateField {
557    pub fn new(
558        field: impl Into<String>,
559        operator: UpdateFieldOperator,
560        value: Option<Arg>,
561        opts: UpdateFieldOptions,
562    ) -> Self {
563        Self {
564            skip: opts.skip,
565            skip_fn: opts.skip_fn,
566            field: field.into(),
567            operator,
568            value,
569            value_fn: opts.value_fn,
570        }
571    }
572}
573
574impl std::fmt::Debug for UpdateField {
575    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
576        f.debug_struct("UpdateField")
577            .field("skip", &self.skip)
578            .field("field", &self.field)
579            .field("operator", &self.operator)
580            .field("value", &self.value)
581            .field("has_skip_fn", &self.skip_fn.is_some())
582            .field("has_value_fn", &self.value_fn.is_some())
583            .finish()
584    }
585}
586
587/// A chain of UpdateField operations.
588#[derive(Debug, Default, Clone)]
589pub struct UpdateFieldChain {
590    fields: Vec<UpdateField>,
591}
592
593impl UpdateFieldChain {
594    pub fn new() -> Self {
595        Self::default()
596    }
597
598    pub fn assign(
599        mut self,
600        field: impl Into<String>,
601        value: impl Into<Arg>,
602        opts: UpdateFieldOptions,
603    ) -> Self {
604        self.fields.push(UpdateField::new(
605            field,
606            UpdateFieldOperator::Assign,
607            Some(value.into()),
608            opts,
609        ));
610        self
611    }
612
613    pub fn incr(mut self, field: impl Into<String>, opts: UpdateFieldOptions) -> Self {
614        self.fields.push(UpdateField::new(
615            field,
616            UpdateFieldOperator::Incr,
617            None,
618            opts,
619        ));
620        self
621    }
622
623    pub fn decr(mut self, field: impl Into<String>, opts: UpdateFieldOptions) -> Self {
624        self.fields.push(UpdateField::new(
625            field,
626            UpdateFieldOperator::Decr,
627            None,
628            opts,
629        ));
630        self
631    }
632
633    pub fn add(
634        mut self,
635        field: impl Into<String>,
636        value: impl Into<Arg>,
637        opts: UpdateFieldOptions,
638    ) -> Self {
639        self.fields.push(UpdateField::new(
640            field,
641            UpdateFieldOperator::Add,
642            Some(value.into()),
643            opts,
644        ));
645        self
646    }
647
648    pub fn sub(
649        mut self,
650        field: impl Into<String>,
651        value: impl Into<Arg>,
652        opts: UpdateFieldOptions,
653    ) -> Self {
654        self.fields.push(UpdateField::new(
655            field,
656            UpdateFieldOperator::Sub,
657            Some(value.into()),
658            opts,
659        ));
660        self
661    }
662
663    pub fn mul(
664        mut self,
665        field: impl Into<String>,
666        value: impl Into<Arg>,
667        opts: UpdateFieldOptions,
668    ) -> Self {
669        self.fields.push(UpdateField::new(
670            field,
671            UpdateFieldOperator::Mul,
672            Some(value.into()),
673            opts,
674        ));
675        self
676    }
677
678    pub fn div(
679        mut self,
680        field: impl Into<String>,
681        value: impl Into<Arg>,
682        opts: UpdateFieldOptions,
683    ) -> Self {
684        self.fields.push(UpdateField::new(
685            field,
686            UpdateFieldOperator::Div,
687            Some(value.into()),
688            opts,
689        ));
690        self
691    }
692
693    pub fn build(self) -> Vec<(String, UpdateValue)> {
694        let mut out = Vec::new();
695        for mut field in self.fields {
696            if let Some(f) = &field.skip_fn {
697                if f() {
698                    continue;
699                }
700            } else if field.skip {
701                continue;
702            }
703            if let Some(f) = &field.value_fn {
704                field.value = Some(f());
705            }
706            out.push((field.field.clone(), UpdateValue::from(field)));
707        }
708        out
709    }
710}
711
712/// Update values payload.
713#[derive(Debug, Clone)]
714pub enum UpdateValue {
715    Field(UpdateField),
716    Value(Arg),
717}
718
719impl From<UpdateField> for UpdateValue {
720    fn from(v: UpdateField) -> Self {
721        Self::Field(v)
722    }
723}
724
725impl<T: Into<Arg>> From<T> for UpdateValue {
726    fn from(v: T) -> Self {
727        Self::Value(v.into())
728    }
729}
730
731/// 将字段名转为字符串切片。
732pub fn to_field_slice(fields: Vec<String>) -> Vec<String> {
733    fields
734}
735
736/// 去除首尾反引号/双引号。
737pub fn unquote(s: &str) -> String {
738    let mut out = s.trim();
739    if out.starts_with('`') || out.starts_with('"') {
740        out = &out[1..];
741    }
742    if out.ends_with('`') || out.ends_with('"') {
743        out = &out[..out.len() - 1];
744    }
745    out.to_string()
746}
747
748/// Quote each segment of a dotted field name according to the flavor.
749pub fn quote_with_flavor(flavor: Flavor, s: &str) -> String {
750    let parts: Vec<String> = s
751        .split('.')
752        .filter(|p| !p.is_empty())
753        .map(|p| flavor.quote(&unquote(p)))
754        .collect();
755    parts.join(".")
756}
757
758fn should_skip(cond: &Condition) -> bool {
759    if let Some(f) = &cond.skip_fn {
760        return f();
761    }
762    cond.skip
763}
764
765fn materialize_value(cond: &Condition) -> ConditionValue {
766    if let Some(f) = &cond.value_fn {
767        f()
768    } else {
769        cond.value.clone()
770    }
771}
772
773fn materialize_or_values(cond: &Condition) -> Vec<ConditionValue> {
774    if let Some(f) = &cond.or_values_fn {
775        f()
776    } else {
777        cond.or_values.clone()
778    }
779}
780
781fn arg_to_string(arg: &Arg) -> Option<String> {
782    match arg {
783        Arg::Value(SqlValue::String(s)) => Some(s.to_string()),
784        Arg::Value(SqlValue::I64(v)) => Some(v.to_string()),
785        Arg::Value(SqlValue::U64(v)) => Some(v.to_string()),
786        Arg::Value(SqlValue::F64(v)) => Some(v.to_string()),
787        Arg::Value(SqlValue::Bool(v)) => Some(v.to_string()),
788        Arg::SqlNamed(v) => Some(format!("@{}", v.name)),
789        Arg::Raw(v) => Some(v.expr.clone()),
790        _ => None,
791    }
792}
793
794fn value_to_strings(value: &ConditionValue) -> Vec<String> {
795    match value {
796        ConditionValue::Single(v) => arg_to_string(v).into_iter().collect(),
797        ConditionValue::List(vs) => vs.iter().filter_map(arg_to_string).collect(),
798    }
799}
800
801fn value_to_i64(value: &ConditionValue) -> Option<i64> {
802    match value {
803        ConditionValue::Single(Arg::Value(SqlValue::I64(v))) => Some(*v),
804        ConditionValue::Single(Arg::Value(SqlValue::U64(v))) => Some(*v as i64),
805        ConditionValue::Single(Arg::Value(SqlValue::F64(v))) => Some(*v as i64),
806        ConditionValue::Single(Arg::Value(SqlValue::Bool(v))) => Some(if *v { 1 } else { 0 }),
807        ConditionValue::Single(Arg::Value(SqlValue::Null)) => Some(0),
808        _ => None,
809    }
810}
811
812fn build_expr(
813    flavor: Flavor,
814    cond: &Cond,
815    field: &str,
816    operator: Operator,
817    value: &ConditionValue,
818) -> Option<String> {
819    let quoted_field = quote_with_flavor(flavor, field);
820    match operator {
821        Operator::Equal => value.first().map(|v| cond.equal(&quoted_field, v)),
822        Operator::NotEqual => value.first().map(|v| cond.not_equal(&quoted_field, v)),
823        Operator::GreaterThan => value.first().map(|v| cond.greater_than(&quoted_field, v)),
824        Operator::LessThan => value.first().map(|v| cond.less_than(&quoted_field, v)),
825        Operator::GreaterEqualThan => value
826            .first()
827            .map(|v| cond.greater_equal_than(&quoted_field, v)),
828        Operator::LessEqualThan => value
829            .first()
830            .map(|v| cond.less_equal_than(&quoted_field, v)),
831        Operator::Like => value.first().map(|v| cond.like(&quoted_field, v)),
832        Operator::NotLike => value.first().map(|v| cond.not_like(&quoted_field, v)),
833        Operator::IsNull => Some(cond.is_null(&quoted_field)),
834        Operator::IsNotNull => Some(cond.is_not_null(&quoted_field)),
835        Operator::Between => value.pair().map(|(l, r)| cond.between(&quoted_field, l, r)),
836        Operator::NotBetween => value
837            .pair()
838            .map(|(l, r)| cond.not_between(&quoted_field, l, r)),
839        Operator::In => {
840            let vals = value.to_vec();
841            if vals.is_empty() {
842                let ph = cond.var(SqlValue::Null);
843                Some(format!("{quoted_field} IN ({ph})"))
844            } else {
845                let phs: Vec<String> = vals.into_iter().map(|v| cond.var(v)).collect();
846                Some(format!("{quoted_field} IN ({})", phs.join(", ")))
847            }
848        }
849        Operator::NotIn => {
850            let vals = value.to_vec();
851            if vals.is_empty() {
852                let ph = cond.var(SqlValue::Null);
853                Some(format!("{quoted_field} NOT IN ({ph})"))
854            } else {
855                let phs: Vec<String> = vals.into_iter().map(|v| cond.var(v)).collect();
856                Some(format!("{quoted_field} NOT IN ({})", phs.join(", ")))
857            }
858        }
859        _ => None,
860    }
861}
862
863fn build_where_clause(flavor: Flavor, conditions: &[Condition]) -> Option<WhereClauseRef> {
864    if conditions.is_empty() {
865        return None;
866    }
867    let wc = WhereClause::new();
868    let cond_builder = Cond::new();
869    let mut has_expr = false;
870
871    for c in conditions {
872        if should_skip(c) {
873            continue;
874        }
875        if let Some(w) = &c.where_clause {
876            wc.borrow_mut().add_where_clause(&w.borrow());
877            has_expr = true;
878            continue;
879        }
880
881        if c.or {
882            let or_values = materialize_or_values(c);
883            let iter_len = c
884                .or_fields
885                .len()
886                .min(c.or_operators.len())
887                .min(or_values.len());
888            let mut exprs = Vec::new();
889            for (i, value) in or_values.iter().enumerate().take(iter_len) {
890                match build_expr(
891                    flavor,
892                    &cond_builder,
893                    &c.or_fields[i],
894                    c.or_operators[i],
895                    value,
896                ) {
897                    Some(expr) if !expr.is_empty() => exprs.push(expr),
898                    _ => {}
899                }
900            }
901            if !exprs.is_empty() {
902                let combined = cond_builder.or(exprs);
903                wc.borrow_mut()
904                    .add_where_expr(cond_builder.args.clone(), [combined]);
905                has_expr = true;
906            }
907        } else {
908            match build_expr(
909                flavor,
910                &cond_builder,
911                &c.field,
912                c.operator,
913                &materialize_value(c),
914            ) {
915                Some(expr) if !expr.is_empty() => {
916                    wc.borrow_mut()
917                        .add_where_expr(cond_builder.args.clone(), [expr]);
918                    has_expr = true;
919                }
920                _ => {}
921            }
922        }
923    }
924
925    if has_expr { Some(wc) } else { None }
926}
927
928fn apply_select_condition(flavor: Flavor, builder: &mut SelectBuilder, condition: &Condition) {
929    if should_skip(condition) {
930        return;
931    }
932    let value = materialize_value(condition);
933    match condition.operator {
934        Operator::Limit => {
935            if let Some(v) = value_to_i64(&value) {
936                builder.limit(v);
937            }
938        }
939        Operator::Offset => {
940            if let Some(v) = value_to_i64(&value) {
941                builder.offset(v);
942            }
943        }
944        Operator::OrderBy => {
945            let cols = value_to_strings(&value);
946            if !cols.is_empty() {
947                builder.order_by(cols);
948            }
949        }
950        Operator::OrderByDesc => {
951            builder.order_by_desc(quote_with_flavor(flavor, &condition.field));
952        }
953        Operator::OrderByAsc => {
954            builder.order_by_asc(quote_with_flavor(flavor, &condition.field));
955        }
956        Operator::GroupBy => {
957            let cols = value_to_strings(&value);
958            if !cols.is_empty() {
959                builder.group_by(cols);
960            } else if !condition.field.is_empty() {
961                builder.group_by(vec![quote_with_flavor(flavor, &condition.field)]);
962            }
963        }
964        Operator::Join => {
965            if let Some(join) = &condition.join {
966                builder.join_with_option(join.option, join.table.clone(), join.on_expr.clone());
967            }
968        }
969        _ => {}
970    }
971}
972
973fn apply_update_condition(flavor: Flavor, builder: &mut UpdateBuilder, condition: &Condition) {
974    if should_skip(condition) {
975        return;
976    }
977    let value = materialize_value(condition);
978    match condition.operator {
979        Operator::Limit => {
980            if let Some(v) = value_to_i64(&value) {
981                builder.limit(v);
982            }
983        }
984        Operator::OrderBy => {
985            let cols = value_to_strings(&value);
986            if !cols.is_empty() {
987                builder.order_by(cols);
988            }
989        }
990        Operator::OrderByDesc => {
991            builder.order_by_desc(quote_with_flavor(flavor, &condition.field));
992        }
993        Operator::OrderByAsc => {
994            builder.order_by_asc(quote_with_flavor(flavor, &condition.field));
995        }
996        _ => {}
997    }
998}
999
1000fn apply_delete_condition(flavor: Flavor, builder: &mut DeleteBuilder, condition: &Condition) {
1001    if should_skip(condition) {
1002        return;
1003    }
1004    let value = materialize_value(condition);
1005    match condition.operator {
1006        Operator::Limit => {
1007            if let Some(v) = value_to_i64(&value) {
1008                builder.limit(v);
1009            }
1010        }
1011        Operator::OrderBy => {
1012            let cols = value_to_strings(&value);
1013            if !cols.is_empty() {
1014                builder.order_by(cols);
1015            }
1016        }
1017        Operator::OrderByDesc => {
1018            builder.order_by_desc(quote_with_flavor(flavor, &condition.field));
1019        }
1020        Operator::OrderByAsc => {
1021            builder.order_by_asc(quote_with_flavor(flavor, &condition.field));
1022        }
1023        _ => {}
1024    }
1025}
1026
1027/// Build SELECT with default flavor.
1028pub fn build_select(
1029    builder: SelectBuilder,
1030    conditions: impl IntoIterator<Item = Condition>,
1031) -> (String, Vec<Arg>) {
1032    build_select_with_flavor(default_flavor(), builder, conditions)
1033}
1034
1035/// Build SELECT with a specific flavor.
1036pub fn build_select_with_flavor(
1037    flavor: Flavor,
1038    mut builder: SelectBuilder,
1039    conditions: impl IntoIterator<Item = Condition>,
1040) -> (String, Vec<Arg>) {
1041    builder.set_flavor(flavor);
1042    let conditions: Vec<Condition> = conditions.into_iter().collect();
1043    if let Some(wc) = build_where_clause(flavor, &conditions) {
1044        builder.add_where_clause_ref(&wc);
1045    }
1046    for c in &conditions {
1047        apply_select_condition(flavor, &mut builder, c);
1048    }
1049    builder.build_with_flavor(flavor, &[])
1050}
1051
1052/// Build UPDATE with default flavor.
1053pub fn build_update(
1054    builder: UpdateBuilder,
1055    data: impl IntoIterator<Item = (impl Into<String>, impl Into<UpdateValue>)>,
1056    conditions: impl IntoIterator<Item = Condition>,
1057) -> (String, Vec<Arg>) {
1058    build_update_with_flavor(default_flavor(), builder, data, conditions)
1059}
1060
1061/// Build UPDATE with a specific flavor.
1062pub fn build_update_with_flavor(
1063    flavor: Flavor,
1064    mut builder: UpdateBuilder,
1065    data: impl IntoIterator<Item = (impl Into<String>, impl Into<UpdateValue>)>,
1066    conditions: impl IntoIterator<Item = Condition>,
1067) -> (String, Vec<Arg>) {
1068    builder.set_flavor(flavor);
1069    let conditions: Vec<Condition> = conditions.into_iter().collect();
1070    if let Some(wc) = build_where_clause(flavor, &conditions) {
1071        builder.add_where_clause_ref(&wc);
1072    }
1073    for c in &conditions {
1074        apply_update_condition(flavor, &mut builder, c);
1075    }
1076
1077    for (field, value) in data {
1078        let field = field.into();
1079        match value.into() {
1080            UpdateValue::Value(v) => {
1081                builder.set_more([builder.assign(&quote_with_flavor(flavor, &field), v)]);
1082            }
1083            UpdateValue::Field(mut f) => {
1084                if let Some(skip_fn) = &f.skip_fn {
1085                    if skip_fn() {
1086                        continue;
1087                    }
1088                } else if f.skip {
1089                    continue;
1090                }
1091                if let Some(func) = &f.value_fn {
1092                    f.value = Some(func());
1093                }
1094                let quoted = quote_with_flavor(flavor, &f.field);
1095                match f.operator {
1096                    UpdateFieldOperator::Assign => {
1097                        if let Some(v) = f.value.clone() {
1098                            builder.set_more([builder.assign(&quoted, v)]);
1099                        }
1100                    }
1101                    UpdateFieldOperator::Incr => {
1102                        builder.set_more([builder.incr(&quoted)]);
1103                    }
1104                    UpdateFieldOperator::Decr => {
1105                        builder.set_more([builder.decr(&quoted)]);
1106                    }
1107                    UpdateFieldOperator::Add => {
1108                        if let Some(v) = f.value.clone() {
1109                            builder.set_more([builder.add_(&quoted, v)]);
1110                        }
1111                    }
1112                    UpdateFieldOperator::Sub => {
1113                        if let Some(v) = f.value.clone() {
1114                            builder.set_more([builder.sub(&quoted, v)]);
1115                        }
1116                    }
1117                    UpdateFieldOperator::Mul => {
1118                        if let Some(v) = f.value.clone() {
1119                            builder.set_more([builder.mul(&quoted, v)]);
1120                        }
1121                    }
1122                    UpdateFieldOperator::Div => {
1123                        if let Some(v) = f.value.clone() {
1124                            builder.set_more([builder.div(&quoted, v)]);
1125                        }
1126                    }
1127                }
1128            }
1129        }
1130    }
1131
1132    builder.build_with_flavor(flavor, &[])
1133}
1134
1135/// Build DELETE with default flavor.
1136pub fn build_delete(
1137    builder: DeleteBuilder,
1138    conditions: impl IntoIterator<Item = Condition>,
1139) -> (String, Vec<Arg>) {
1140    build_delete_with_flavor(default_flavor(), builder, conditions)
1141}
1142
1143/// Build DELETE with a specific flavor.
1144pub fn build_delete_with_flavor(
1145    flavor: Flavor,
1146    mut builder: DeleteBuilder,
1147    conditions: impl IntoIterator<Item = Condition>,
1148) -> (String, Vec<Arg>) {
1149    builder.set_flavor(flavor);
1150    let conditions: Vec<Condition> = conditions.into_iter().collect();
1151    if let Some(wc) = build_where_clause(flavor, &conditions) {
1152        builder.add_where_clause_ref(&wc);
1153    }
1154    for c in &conditions {
1155        apply_delete_condition(flavor, &mut builder, c);
1156    }
1157    builder.build_with_flavor(flavor, &[])
1158}
1159
1160#[cfg(test)]
1161mod tests {
1162    use super::*;
1163    use crate::select::SelectBuilder;
1164    use crate::update::UpdateBuilder;
1165    use crate::{DeleteBuilder, flavor::set_default_flavor};
1166    use pretty_assertions::assert_eq;
1167
1168    #[test]
1169    fn select_with_condition_like_go() {
1170        set_default_flavor(Flavor::MySQL);
1171        let between = vec![
1172            ConditionValue::from(vec![24_i64, 48]),
1173            ConditionValue::from(vec![170_i64, 175]),
1174        ];
1175        let conditions = vec![
1176            Condition::new("name", Operator::Equal, "jaronnie"),
1177            Condition {
1178                skip: false,
1179                skip_fn: None,
1180                or: true,
1181                or_operators: vec![Operator::Between, Operator::Between],
1182                or_fields: vec!["age".into(), "height".into()],
1183                or_values: between,
1184                or_values_fn: None,
1185                field: String::new(),
1186                operator: Operator::Between,
1187                value: ConditionValue::default(),
1188                value_fn: None,
1189                join: None,
1190                where_clause: None,
1191            },
1192        ];
1193
1194        let mut sb = SelectBuilder::new();
1195        sb.select(vec!["name", "age", "height"]).from(vec!["user"]);
1196        let (sql, args) = build_select(sb, conditions);
1197        assert_eq!(
1198            "SELECT name, age, height FROM user WHERE `name` = ? AND (`age` BETWEEN ? AND ? OR `height` BETWEEN ? AND ?)",
1199            sql
1200        );
1201        assert_eq!(
1202            args,
1203            vec![
1204                Arg::from("jaronnie"),
1205                Arg::from(24_i64),
1206                Arg::from(48_i64),
1207                Arg::from(170_i64),
1208                Arg::from(175_i64)
1209            ]
1210        );
1211    }
1212
1213    #[test]
1214    fn chain_basic_and_order() {
1215        let chain = Chain::new()
1216            .equal_opts("field1", "value1", ChainOptions::default().skip(true))
1217            .equal("field2", "value2")
1218            .order_by_desc("create_time")
1219            .order_by_asc("sort");
1220        let mut sb = SelectBuilder::new();
1221        sb.select(vec!["name", "age"]).from(vec!["user"]);
1222        let (sql, args) = build_select_with_flavor(Flavor::MySQL, sb, chain.build());
1223        assert_eq!(
1224            "SELECT name, age FROM user WHERE `field2` = ? ORDER BY `create_time` DESC, `sort` ASC",
1225            sql
1226        );
1227        assert_eq!(args, vec![Arg::from("value2")]);
1228    }
1229
1230    #[test]
1231    fn chain_join_and_null() {
1232        let chain = Chain::new()
1233            .equal("user.field", "value2")
1234            .join(
1235                JoinOption::InnerJoin,
1236                "user_info",
1237                ["user.id = user_info.user_id"],
1238            )
1239            .is_null("delete_at")
1240            .is_not_null("updated_at");
1241        let mut sb = SelectBuilder::new();
1242        sb.select(vec!["user.name", "user.age"]).from(vec!["user"]);
1243        let (sql, args) = build_select_with_flavor(Flavor::MySQL, sb, chain.build());
1244        assert_eq!(
1245            "SELECT user.name, user.age FROM user INNER JOIN user_info ON user.id = user_info.user_id WHERE `user`.`field` = ? AND `delete_at` IS NULL AND `updated_at` IS NOT NULL",
1246            sql
1247        );
1248        assert_eq!(args, vec![Arg::from("value2")]);
1249    }
1250
1251    #[test]
1252    fn chain_equal_fluent_modifiers() {
1253        let chain = Chain::new()
1254            .equal("name", "placeholder")
1255            .value_fn(|| ConditionValue::from("jzero"))
1256            .skip(false)
1257            .skip_fn(|| false);
1258
1259        let mut sb = SelectBuilder::new();
1260        sb.select(vec!["id", "name"]).from(vec!["user"]);
1261        let (sql, args) = build_select_with_flavor(Flavor::MySQL, sb, chain.build());
1262
1263        assert_eq!("SELECT id, name FROM user WHERE `name` = ?", sql);
1264        assert_eq!(args, vec![Arg::from("jzero")]);
1265    }
1266
1267    #[test]
1268    fn chain_page_and_group_by() {
1269        let chain = Chain::new()
1270            .equal("status", "active")
1271            .group_by("status")
1272            .page(2, 10)
1273            .order_by(vec!["status"]);
1274
1275        let mut sb = SelectBuilder::new();
1276        sb.select(vec!["status", "COUNT(1)"]).from(vec!["users"]);
1277        let (sql, args) = build_select_with_flavor(Flavor::MySQL, sb, chain.build());
1278
1279        assert_eq!(
1280            "SELECT status, COUNT(1) FROM users WHERE `status` = ? GROUP BY `status` ORDER BY status LIMIT ? OFFSET ?",
1281            sql
1282        );
1283        assert_eq!(
1284            args,
1285            vec![Arg::from("active"), Arg::from(10_i64), Arg::from(10_i64)]
1286        );
1287    }
1288
1289    #[test]
1290    fn condition_value_fn_and_skip_fn() {
1291        let conds = vec![
1292            Condition {
1293                skip: false,
1294                skip_fn: Some(Arc::new(|| true)),
1295                or: false,
1296                or_operators: Vec::new(),
1297                or_fields: Vec::new(),
1298                or_values: Vec::new(),
1299                or_values_fn: None,
1300                field: "skip_me".into(),
1301                operator: Operator::Equal,
1302                value: ConditionValue::from("never"),
1303                value_fn: None,
1304                join: None,
1305                where_clause: None,
1306            },
1307            Condition {
1308                skip: false,
1309                skip_fn: None,
1310                or: false,
1311                or_operators: Vec::new(),
1312                or_fields: Vec::new(),
1313                or_values: Vec::new(),
1314                or_values_fn: None,
1315                field: "name".into(),
1316                operator: Operator::Equal,
1317                value: ConditionValue::from("placeholder"),
1318                value_fn: Some(Arc::new(|| ConditionValue::from("dynamic"))),
1319                join: None,
1320                where_clause: None,
1321            },
1322        ];
1323
1324        let mut sb = SelectBuilder::new();
1325        sb.select(vec!["id"]).from(vec!["users"]);
1326        let (sql, args) = build_select_with_flavor(Flavor::MySQL, sb, conds);
1327
1328        assert_eq!("SELECT id FROM users WHERE `name` = ?", sql);
1329        assert_eq!(args, vec![Arg::from("dynamic")]);
1330    }
1331
1332    #[test]
1333    fn condition_delete_skip_and_value_func() {
1334        let conds = vec![
1335            Condition {
1336                skip: false,
1337                skip_fn: Some(Arc::new(|| true)),
1338                or: false,
1339                or_operators: Vec::new(),
1340                or_fields: Vec::new(),
1341                or_values: Vec::new(),
1342                or_values_fn: None,
1343                field: "name".into(),
1344                operator: Operator::Equal,
1345                value: ConditionValue::from("jaronnie"),
1346                value_fn: Some(Arc::new(|| ConditionValue::from("jaronnie2"))),
1347                join: None,
1348                where_clause: None,
1349            },
1350            Condition {
1351                skip: false,
1352                skip_fn: None,
1353                or: true,
1354                or_operators: vec![Operator::Between, Operator::Between],
1355                or_fields: vec!["age".into(), "height".into()],
1356                or_values: vec![
1357                    ConditionValue::from(vec![24_i64, 48]),
1358                    ConditionValue::from(vec![170_i64, 175]),
1359                ],
1360                or_values_fn: Some(Arc::new(|| {
1361                    vec![
1362                        ConditionValue::from(vec![24_i64, 49]),
1363                        ConditionValue::from(vec![170_i64, 176]),
1364                    ]
1365                })),
1366                field: String::new(),
1367                operator: Operator::Between,
1368                value: ConditionValue::default(),
1369                value_fn: None,
1370                join: None,
1371                where_clause: None,
1372            },
1373        ];
1374        let mut db = DeleteBuilder::new();
1375        db.delete_from(vec!["user"]);
1376        let (sql, args) = build_delete(db, conds);
1377        assert_eq!(
1378            "DELETE FROM user WHERE (`age` BETWEEN ? AND ? OR `height` BETWEEN ? AND ?)",
1379            sql
1380        );
1381        assert_eq!(
1382            args,
1383            vec![
1384                Arg::from(24_i64),
1385                Arg::from(49_i64),
1386                Arg::from(170_i64),
1387                Arg::from(176_i64)
1388            ]
1389        );
1390    }
1391
1392    #[test]
1393    fn update_with_update_field_chain() {
1394        let data = UpdateFieldChain::new()
1395            .assign("name", "jaronnie", UpdateFieldOptions::default().skip(true))
1396            .incr("version", UpdateFieldOptions::default())
1397            .add(
1398                "age",
1399                12_i64,
1400                UpdateFieldOptions::default().value_fn(|| Arg::from(15_i64)),
1401            )
1402            .build();
1403
1404        let chain = Chain::new().equal("id", 1_i64);
1405        let mut ub = UpdateBuilder::new();
1406        ub.update(vec!["users"]);
1407        let (sql, args) = build_update_with_flavor(Flavor::MySQL, ub, data, chain.build());
1408        assert_eq!(
1409            "UPDATE users SET `version` = `version` + 1, `age` = `age` + ? WHERE `id` = ?",
1410            sql
1411        );
1412        assert_eq!(args, vec![Arg::from(15_i64), Arg::from(1_i64)]);
1413    }
1414
1415    #[test]
1416    fn condition_in_allows_empty_slice() {
1417        let mut sb = SelectBuilder::new();
1418        sb.select(vec!["id"]).from(vec!["users"]);
1419        let (sql, args) = build_select(
1420            sb,
1421            [Condition::new(
1422                "id",
1423                Operator::In,
1424                ConditionValue::from(Vec::<i64>::new()),
1425            )],
1426        );
1427        assert_eq!("SELECT id FROM users WHERE `id` IN (?)", sql);
1428        assert_eq!(args, vec![Arg::from(SqlValue::Null)]);
1429    }
1430}