Skip to main content

yang_db/mysql/
condition.rs

1use chrono::NaiveDateTime;
2use serde_json::Value as JsonValue;
3
4/// SQL 值类型
5#[derive(Debug, Clone)]
6pub enum SqlValue {
7    Null,
8    Bool(bool),
9    Int(i64),
10    Float(f64),
11    String(String),
12    Bytes(Vec<u8>),
13    Json(JsonValue),
14    DateTime(NaiveDateTime),
15    Timestamp(i64),
16}
17
18/// 查询条件
19#[derive(Debug, Clone)]
20pub enum Condition {
21    /// 相等
22    Eq(String, SqlValue),
23    /// 不等
24    Ne(String, SqlValue),
25    /// 大于
26    Gt(String, SqlValue),
27    /// 小于
28    Lt(String, SqlValue),
29    /// 大于等于
30    Gte(String, SqlValue),
31    /// 小于等于
32    Lte(String, SqlValue),
33    /// IN 条件
34    In(String, Vec<SqlValue>),
35    /// BETWEEN 条件
36    Between(String, SqlValue, SqlValue),
37    /// LIKE 条件
38    Like(String, String),
39    /// IS NULL 条件
40    IsNull(String),
41    /// IS NOT NULL 条件
42    IsNotNull(String),
43    /// AND 组合
44    And(Vec<Condition>),
45    /// OR 组合
46    Or(Vec<Condition>),
47}
48
49// 实现 From trait 支持自动转换
50impl From<i32> for SqlValue {
51    fn from(v: i32) -> Self {
52        SqlValue::Int(v as i64)
53    }
54}
55
56impl From<i64> for SqlValue {
57    fn from(v: i64) -> Self {
58        SqlValue::Int(v)
59    }
60}
61
62impl From<u64> for SqlValue {
63    fn from(v: u64) -> Self {
64        SqlValue::Int(v as i64)
65    }
66}
67
68impl From<f64> for SqlValue {
69    fn from(v: f64) -> Self {
70        SqlValue::Float(v)
71    }
72}
73
74impl From<f32> for SqlValue {
75    fn from(v: f32) -> Self {
76        SqlValue::Float(v as f64)
77    }
78}
79
80impl From<String> for SqlValue {
81    fn from(v: String) -> Self {
82        SqlValue::String(v)
83    }
84}
85
86impl From<&str> for SqlValue {
87    fn from(v: &str) -> Self {
88        SqlValue::String(v.to_string())
89    }
90}
91
92impl From<bool> for SqlValue {
93    fn from(v: bool) -> Self {
94        SqlValue::Bool(v)
95    }
96}
97
98impl From<Vec<u8>> for SqlValue {
99    fn from(v: Vec<u8>) -> Self {
100        SqlValue::Bytes(v)
101    }
102}
103
104impl From<JsonValue> for SqlValue {
105    fn from(v: JsonValue) -> Self {
106        SqlValue::Json(v)
107    }
108}
109
110impl From<NaiveDateTime> for SqlValue {
111    fn from(v: NaiveDateTime) -> Self {
112        SqlValue::DateTime(v)
113    }
114}
115
116impl<T> From<Option<T>> for SqlValue
117where
118    T: Into<SqlValue>,
119{
120    fn from(v: Option<T>) -> Self {
121        match v {
122            Some(val) => val.into(),
123            None => SqlValue::Null,
124        }
125    }
126}
127
128/// 将条件转换为 SQL 字符串和参数列表
129///
130/// # 参数
131/// - condition: 要转换的条件
132/// - params: 用于收集参数的可变向量
133///
134/// # 返回
135/// - SQL 字符串片段
136pub fn condition_to_sql(condition: &Condition, params: &mut Vec<SqlValue>) -> String {
137    match condition {
138        Condition::Eq(field, value) => {
139            params.push(value.clone());
140            format!("{} = ?", field)
141        }
142        Condition::Ne(field, value) => {
143            params.push(value.clone());
144            format!("{} != ?", field)
145        }
146        Condition::Gt(field, value) => {
147            params.push(value.clone());
148            format!("{} > ?", field)
149        }
150        Condition::Lt(field, value) => {
151            params.push(value.clone());
152            format!("{} < ?", field)
153        }
154        Condition::Gte(field, value) => {
155            params.push(value.clone());
156            format!("{} >= ?", field)
157        }
158        Condition::Lte(field, value) => {
159            params.push(value.clone());
160            format!("{} <= ?", field)
161        }
162        Condition::In(field, values) => {
163            if values.is_empty() {
164                // IN 空列表总是返回 false
165                return "1 = 0".to_string();
166            }
167            let placeholders = values
168                .iter()
169                .map(|v| {
170                    params.push(v.clone());
171                    "?"
172                })
173                .collect::<Vec<_>>()
174                .join(", ");
175            format!("{} IN ({})", field, placeholders)
176        }
177        Condition::Between(field, start, end) => {
178            params.push(start.clone());
179            params.push(end.clone());
180            format!("{} BETWEEN ? AND ?", field)
181        }
182        Condition::Like(field, pattern) => {
183            params.push(SqlValue::String(pattern.clone()));
184            format!("{} LIKE ?", field)
185        }
186        Condition::IsNull(field) => format!("{} IS NULL", field),
187        Condition::IsNotNull(field) => format!("{} IS NOT NULL", field),
188        Condition::And(conditions) => {
189            if conditions.is_empty() {
190                return "1 = 1".to_string();
191            }
192            if conditions.len() == 1 {
193                return condition_to_sql(&conditions[0], params);
194            }
195            // AND 条件需要括号以确保优先级
196            let parts: Vec<String> = conditions
197                .iter()
198                .map(|c| condition_to_sql(c, params))
199                .collect();
200            format!("({})", parts.join(" AND "))
201        }
202        Condition::Or(conditions) => {
203            if conditions.is_empty() {
204                return "1 = 0".to_string();
205            }
206            if conditions.len() == 1 {
207                return condition_to_sql(&conditions[0], params);
208            }
209            // OR 条件需要括号
210            let parts: Vec<String> = conditions
211                .iter()
212                .map(|c| condition_to_sql(c, params))
213                .collect();
214            format!("({})", parts.join(" OR "))
215        }
216    }
217}
218
219/// 消费版本的条件转 SQL 函数,避免不必要的 clone 开销
220///
221/// 与 `condition_to_sql` 的借用版本相比,本函数消费传入的 `Condition`,
222/// 对 `SqlValue::String`、`SqlValue::Bytes`、`SqlValue::Json` 等堆分配类型
223/// 直接 push 到 params 中,无需 clone,从而减少堆分配次数。
224///
225/// # 参数
226/// - `condition`: 要消费的条件(owned)
227/// - `params`: 用于收集参数的可变向量
228///
229/// # 返回
230/// - SQL 字符串片段
231pub fn condition_to_sql_owned(condition: Condition, params: &mut Vec<SqlValue>) -> String {
232    match condition {
233        Condition::Eq(field, value) => {
234            // 直接 push,无需 clone
235            params.push(value);
236            format!("{} = ?", field)
237        }
238        Condition::Ne(field, value) => {
239            params.push(value);
240            format!("{} != ?", field)
241        }
242        Condition::Gt(field, value) => {
243            params.push(value);
244            format!("{} > ?", field)
245        }
246        Condition::Lt(field, value) => {
247            params.push(value);
248            format!("{} < ?", field)
249        }
250        Condition::Gte(field, value) => {
251            params.push(value);
252            format!("{} >= ?", field)
253        }
254        Condition::Lte(field, value) => {
255            params.push(value);
256            format!("{} <= ?", field)
257        }
258        Condition::In(field, values) => {
259            if values.is_empty() {
260                // IN 空列表总是返回 false
261                return "1 = 0".to_string();
262            }
263            let count = values.len();
264            // 直接 extend,无需逐个 clone
265            params.extend(values);
266            let placeholders = vec!["?"; count].join(", ");
267            format!("{} IN ({})", field, placeholders)
268        }
269        Condition::Between(field, start, end) => {
270            // 直接 push 两个值,无需 clone
271            params.push(start);
272            params.push(end);
273            format!("{} BETWEEN ? AND ?", field)
274        }
275        Condition::Like(field, pattern) => {
276            // pattern 是 String,直接消费
277            params.push(SqlValue::String(pattern));
278            format!("{} LIKE ?", field)
279        }
280        Condition::IsNull(field) => format!("{} IS NULL", field),
281        Condition::IsNotNull(field) => format!("{} IS NOT NULL", field),
282        Condition::And(conditions) => {
283            if conditions.is_empty() {
284                return "1 = 1".to_string();
285            }
286            if conditions.len() == 1 {
287                // 只有一个条件时,直接递归处理,避免多余括号
288                let mut iter = conditions.into_iter();
289                return condition_to_sql_owned(iter.next().unwrap(), params);
290            }
291            // AND 条件需要括号以确保优先级,递归调用自身消费子条件
292            let parts: Vec<String> = conditions
293                .into_iter()
294                .map(|c| condition_to_sql_owned(c, params))
295                .collect();
296            format!("({})", parts.join(" AND "))
297        }
298        Condition::Or(conditions) => {
299            if conditions.is_empty() {
300                return "1 = 0".to_string();
301            }
302            if conditions.len() == 1 {
303                // 只有一个条件时,直接递归处理,避免多余括号
304                let mut iter = conditions.into_iter();
305                return condition_to_sql_owned(iter.next().unwrap(), params);
306            }
307            // OR 条件需要括号,递归调用自身消费子条件
308            let parts: Vec<String> = conditions
309                .into_iter()
310                .map(|c| condition_to_sql_owned(c, params))
311                .collect();
312            format!("({})", parts.join(" OR "))
313        }
314    }
315}
316
317#[cfg(test)]
318mod tests {
319    use super::*;
320    use chrono::NaiveDate;
321
322    #[test]
323    fn test_from_i32() {
324        let value: SqlValue = 42i32.into();
325        match value {
326            SqlValue::Int(v) => assert_eq!(v, 42),
327            _ => panic!("期望 SqlValue::Int"),
328        }
329    }
330
331    #[test]
332    fn test_from_i64() {
333        let value: SqlValue = 9223372036854775807i64.into();
334        match value {
335            SqlValue::Int(v) => assert_eq!(v, 9223372036854775807),
336            _ => panic!("期望 SqlValue::Int"),
337        }
338    }
339
340    #[test]
341    fn test_from_f32() {
342        let value: SqlValue = 3.5f32.into();
343        match value {
344            SqlValue::Float(v) => assert!((v - 3.5).abs() < 0.01),
345            _ => panic!("期望 SqlValue::Float"),
346        }
347    }
348
349    #[test]
350    fn test_from_f64() {
351        let value: SqlValue = 2.5f64.into();
352        match value {
353            SqlValue::Float(v) => assert!((v - 2.5).abs() < 0.000001),
354            _ => panic!("期望 SqlValue::Float"),
355        }
356    }
357
358    #[test]
359    fn test_from_string() {
360        let value: SqlValue = String::from("测试字符串").into();
361        match value {
362            SqlValue::String(s) => assert_eq!(s, "测试字符串"),
363            _ => panic!("期望 SqlValue::String"),
364        }
365    }
366
367    #[test]
368    fn test_from_str() {
369        let value: SqlValue = "hello world".into();
370        match value {
371            SqlValue::String(s) => assert_eq!(s, "hello world"),
372            _ => panic!("期望 SqlValue::String"),
373        }
374    }
375
376    #[test]
377    fn test_from_bool_true() {
378        let value: SqlValue = true.into();
379        match value {
380            SqlValue::Bool(b) => assert!(b),
381            _ => panic!("期望 SqlValue::Bool"),
382        }
383    }
384
385    #[test]
386    fn test_from_bool_false() {
387        let value: SqlValue = false.into();
388        match value {
389            SqlValue::Bool(b) => assert!(!b),
390            _ => panic!("期望 SqlValue::Bool"),
391        }
392    }
393
394    #[test]
395    fn test_from_vec_u8() {
396        let bytes = vec![0x48, 0x65, 0x6c, 0x6c, 0x6f]; // "Hello"
397        let value: SqlValue = bytes.clone().into();
398        match value {
399            SqlValue::Bytes(b) => assert_eq!(b, bytes),
400            _ => panic!("期望 SqlValue::Bytes"),
401        }
402    }
403
404    #[test]
405    fn test_from_json_value() {
406        let json = serde_json::json!({
407            "name": "测试",
408            "age": 25,
409            "active": true
410        });
411        let value: SqlValue = json.clone().into();
412        match value {
413            SqlValue::Json(j) => assert_eq!(j, json),
414            _ => panic!("期望 SqlValue::Json"),
415        }
416    }
417
418    #[test]
419    fn test_from_naive_datetime() {
420        let dt = NaiveDate::from_ymd_opt(2024, 1, 15)
421            .unwrap()
422            .and_hms_opt(10, 30, 45)
423            .unwrap();
424        let value: SqlValue = dt.into();
425        match value {
426            SqlValue::DateTime(d) => assert_eq!(d, dt),
427            _ => panic!("期望 SqlValue::DateTime"),
428        }
429    }
430
431    #[test]
432    fn test_from_option_some() {
433        let value: SqlValue = Some(42i32).into();
434        match value {
435            SqlValue::Int(v) => assert_eq!(v, 42),
436            _ => panic!("期望 SqlValue::Int"),
437        }
438    }
439
440    #[test]
441    fn test_from_option_none() {
442        let value: SqlValue = None::<i32>.into();
443        match value {
444            SqlValue::Null => (),
445            _ => panic!("期望 SqlValue::Null"),
446        }
447    }
448
449    #[test]
450    fn test_from_option_string_some() {
451        let value: SqlValue = Some(String::from("测试")).into();
452        match value {
453            SqlValue::String(s) => assert_eq!(s, "测试"),
454            _ => panic!("期望 SqlValue::String"),
455        }
456    }
457
458    #[test]
459    fn test_from_option_string_none() {
460        let value: SqlValue = None::<String>.into();
461        match value {
462            SqlValue::Null => (),
463            _ => panic!("期望 SqlValue::Null"),
464        }
465    }
466
467    #[test]
468    fn test_negative_integers() {
469        let value: SqlValue = (-100i32).into();
470        match value {
471            SqlValue::Int(v) => assert_eq!(v, -100),
472            _ => panic!("期望 SqlValue::Int"),
473        }
474    }
475
476    #[test]
477    fn test_negative_floats() {
478        let value: SqlValue = (-3.5f64).into();
479        match value {
480            SqlValue::Float(v) => assert!((v + 3.5).abs() < 0.01),
481            _ => panic!("期望 SqlValue::Float"),
482        }
483    }
484
485    #[test]
486    fn test_empty_string() {
487        let value: SqlValue = "".into();
488        match value {
489            SqlValue::String(s) => assert_eq!(s, ""),
490            _ => panic!("期望 SqlValue::String"),
491        }
492    }
493
494    #[test]
495    fn test_empty_bytes() {
496        let value: SqlValue = Vec::<u8>::new().into();
497        match value {
498            SqlValue::Bytes(b) => assert!(b.is_empty()),
499            _ => panic!("期望 SqlValue::Bytes"),
500        }
501    }
502
503    #[test]
504    fn test_json_null() {
505        let json = serde_json::Value::Null;
506        let value: SqlValue = json.into();
507        match value {
508            SqlValue::Json(j) => assert!(j.is_null()),
509            _ => panic!("期望 SqlValue::Json"),
510        }
511    }
512
513    #[test]
514    fn test_json_array() {
515        let json = serde_json::json!([1, 2, 3, 4, 5]);
516        let value: SqlValue = json.clone().into();
517        match value {
518            SqlValue::Json(j) => assert_eq!(j, json),
519            _ => panic!("期望 SqlValue::Json"),
520        }
521    }
522
523    #[test]
524    fn test_unicode_string() {
525        let value: SqlValue = "你好世界 🌍".into();
526        match value {
527            SqlValue::String(s) => assert_eq!(s, "你好世界 🌍"),
528            _ => panic!("期望 SqlValue::String"),
529        }
530    }
531
532    #[test]
533    fn test_zero_values() {
534        let int_value: SqlValue = 0i32.into();
535        match int_value {
536            SqlValue::Int(v) => assert_eq!(v, 0),
537            _ => panic!("期望 SqlValue::Int"),
538        }
539
540        let float_value: SqlValue = 0.0f64.into();
541        match float_value {
542            SqlValue::Float(v) => assert_eq!(v, 0.0),
543            _ => panic!("期望 SqlValue::Float"),
544        }
545    }
546
547    // 测试 condition_to_sql 函数
548    #[test]
549    fn test_condition_eq() {
550        let mut params = Vec::new();
551        let cond = Condition::Eq("name".to_string(), SqlValue::String("test".to_string()));
552        let sql = super::condition_to_sql(&cond, &mut params);
553        assert_eq!(sql, "name = ?");
554        assert_eq!(params.len(), 1);
555    }
556
557    #[test]
558    fn test_condition_ne() {
559        let mut params = Vec::new();
560        let cond = Condition::Ne("status".to_string(), SqlValue::Int(1));
561        let sql = super::condition_to_sql(&cond, &mut params);
562        assert_eq!(sql, "status != ?");
563        assert_eq!(params.len(), 1);
564    }
565
566    #[test]
567    fn test_condition_gt() {
568        let mut params = Vec::new();
569        let cond = Condition::Gt("age".to_string(), SqlValue::Int(18));
570        let sql = super::condition_to_sql(&cond, &mut params);
571        assert_eq!(sql, "age > ?");
572        assert_eq!(params.len(), 1);
573    }
574
575    #[test]
576    fn test_condition_lt() {
577        let mut params = Vec::new();
578        let cond = Condition::Lt("price".to_string(), SqlValue::Float(100.0));
579        let sql = super::condition_to_sql(&cond, &mut params);
580        assert_eq!(sql, "price < ?");
581        assert_eq!(params.len(), 1);
582    }
583
584    #[test]
585    fn test_condition_gte() {
586        let mut params = Vec::new();
587        let cond = Condition::Gte("score".to_string(), SqlValue::Int(60));
588        let sql = super::condition_to_sql(&cond, &mut params);
589        assert_eq!(sql, "score >= ?");
590        assert_eq!(params.len(), 1);
591    }
592
593    #[test]
594    fn test_condition_lte() {
595        let mut params = Vec::new();
596        let cond = Condition::Lte("count".to_string(), SqlValue::Int(10));
597        let sql = super::condition_to_sql(&cond, &mut params);
598        assert_eq!(sql, "count <= ?");
599        assert_eq!(params.len(), 1);
600    }
601
602    #[test]
603    fn test_condition_in() {
604        let mut params = Vec::new();
605        let cond = Condition::In(
606            "id".to_string(),
607            vec![SqlValue::Int(1), SqlValue::Int(2), SqlValue::Int(3)],
608        );
609        let sql = super::condition_to_sql(&cond, &mut params);
610        assert_eq!(sql, "id IN (?, ?, ?)");
611        assert_eq!(params.len(), 3);
612    }
613
614    #[test]
615    fn test_condition_in_empty() {
616        let mut params = Vec::new();
617        let cond = Condition::In("id".to_string(), vec![]);
618        let sql = super::condition_to_sql(&cond, &mut params);
619        assert_eq!(sql, "1 = 0");
620        assert_eq!(params.len(), 0);
621    }
622
623    #[test]
624    fn test_condition_between() {
625        let mut params = Vec::new();
626        let cond = Condition::Between("age".to_string(), SqlValue::Int(18), SqlValue::Int(65));
627        let sql = super::condition_to_sql(&cond, &mut params);
628        assert_eq!(sql, "age BETWEEN ? AND ?");
629        assert_eq!(params.len(), 2);
630    }
631
632    #[test]
633    fn test_condition_like() {
634        let mut params = Vec::new();
635        let cond = Condition::Like("name".to_string(), "%test%".to_string());
636        let sql = super::condition_to_sql(&cond, &mut params);
637        assert_eq!(sql, "name LIKE ?");
638        assert_eq!(params.len(), 1);
639    }
640
641    #[test]
642    fn test_condition_and() {
643        let mut params = Vec::new();
644        let cond = Condition::And(vec![
645            Condition::Eq("name".to_string(), SqlValue::String("test".to_string())),
646            Condition::Gt("age".to_string(), SqlValue::Int(18)),
647        ]);
648        let sql = super::condition_to_sql(&cond, &mut params);
649        assert_eq!(sql, "(name = ? AND age > ?)");
650        assert_eq!(params.len(), 2);
651    }
652
653    #[test]
654    fn test_condition_or() {
655        let mut params = Vec::new();
656        let cond = Condition::Or(vec![
657            Condition::Eq("status".to_string(), SqlValue::Int(1)),
658            Condition::Eq("status".to_string(), SqlValue::Int(2)),
659        ]);
660        let sql = super::condition_to_sql(&cond, &mut params);
661        assert_eq!(sql, "(status = ? OR status = ?)");
662        assert_eq!(params.len(), 2);
663    }
664
665    #[test]
666    fn test_condition_and_or_priority() {
667        let mut params = Vec::new();
668        // (name = 'test' OR name = 'demo') AND age > 18
669        let cond = Condition::And(vec![
670            Condition::Or(vec![
671                Condition::Eq("name".to_string(), SqlValue::String("test".to_string())),
672                Condition::Eq("name".to_string(), SqlValue::String("demo".to_string())),
673            ]),
674            Condition::Gt("age".to_string(), SqlValue::Int(18)),
675        ]);
676        let sql = super::condition_to_sql(&cond, &mut params);
677        // OR 条件应该被括号包围
678        assert_eq!(sql, "((name = ? OR name = ?) AND age > ?)");
679        assert_eq!(params.len(), 3);
680    }
681
682    #[test]
683    fn test_condition_empty_and() {
684        let mut params = Vec::new();
685        let cond = Condition::And(vec![]);
686        let sql = super::condition_to_sql(&cond, &mut params);
687        assert_eq!(sql, "1 = 1");
688        assert_eq!(params.len(), 0);
689    }
690
691    #[test]
692    fn test_condition_empty_or() {
693        let mut params = Vec::new();
694        let cond = Condition::Or(vec![]);
695        let sql = super::condition_to_sql(&cond, &mut params);
696        assert_eq!(sql, "1 = 0");
697        assert_eq!(params.len(), 0);
698    }
699
700    #[test]
701    fn test_condition_single_and() {
702        let mut params = Vec::new();
703        let cond = Condition::And(vec![Condition::Eq("id".to_string(), SqlValue::Int(1))]);
704        let sql = super::condition_to_sql(&cond, &mut params);
705        assert_eq!(sql, "id = ?");
706        assert_eq!(params.len(), 1);
707    }
708
709    #[test]
710    fn test_condition_single_or() {
711        let mut params = Vec::new();
712        let cond = Condition::Or(vec![Condition::Eq("id".to_string(), SqlValue::Int(1))]);
713        let sql = super::condition_to_sql(&cond, &mut params);
714        assert_eq!(sql, "id = ?");
715        assert_eq!(params.len(), 1);
716    }
717}
718
719#[cfg(test)]
720mod property_tests {
721    use super::*;
722    use proptest::prelude::*;
723
724    // 生成有效的字段名(字母开头,后跟字母数字下划线)
725    fn field_name_strategy() -> impl Strategy<Value = String> {
726        "[a-z][a-z0-9_]{0,30}"
727    }
728
729    // 生成 SqlValue 策略
730    fn sql_value_strategy() -> impl Strategy<Value = SqlValue> {
731        prop_oneof![
732            Just(SqlValue::Null),
733            any::<bool>().prop_map(SqlValue::Bool),
734            any::<i64>().prop_map(SqlValue::Int),
735            any::<f64>().prop_map(|f| {
736                if f.is_finite() {
737                    SqlValue::Float(f)
738                } else {
739                    SqlValue::Float(0.0)
740                }
741            }),
742            "[a-zA-Z0-9_\\s]{0,50}".prop_map(SqlValue::String),
743        ]
744    }
745
746    // **Feature: mysql-query-builder, Property 5: 操作符支持**
747    // **验证需求:3.3**
748    //
749    // 属性:对于任意支持的操作符(=, !=, >, <, >=, <=, in, between, like),
750    // 生成的 SQL 应该包含正确的操作符语法
751    //
752    // 此测试验证所有支持的操作符都能正确生成 SQL 语句,
753    // 确保参数化查询的正确性和 SQL 注入防护。
754    proptest! {
755        #![proptest_config(ProptestConfig::with_cases(100))]
756
757        #[test]
758        fn prop_operator_eq_support(
759            field in field_name_strategy(),
760            value in sql_value_strategy()
761        ) {
762            let mut params = Vec::new();
763            let cond = Condition::Eq(field.clone(), value);
764            let sql = condition_to_sql(&cond, &mut params);
765
766            // 验证 SQL 包含正确的操作符
767            let expected = format!("{} = {{placeholder}}", field).replace("{placeholder}", "?");
768            prop_assert!(sql.contains(&expected));
769            prop_assert_eq!(params.len(), 1);
770        }
771
772        #[test]
773        fn prop_operator_ne_support(
774            field in field_name_strategy(),
775            value in sql_value_strategy()
776        ) {
777            let mut params = Vec::new();
778            let cond = Condition::Ne(field.clone(), value);
779            let sql = condition_to_sql(&cond, &mut params);
780
781            let expected = format!("{} != {{placeholder}}", field).replace("{placeholder}", "?");
782            prop_assert!(sql.contains(&expected));
783            prop_assert_eq!(params.len(), 1);
784        }
785
786        #[test]
787        fn prop_operator_gt_support(
788            field in field_name_strategy(),
789            value in sql_value_strategy()
790        ) {
791            let mut params = Vec::new();
792            let cond = Condition::Gt(field.clone(), value);
793            let sql = condition_to_sql(&cond, &mut params);
794
795            let expected = format!("{} > {{placeholder}}", field).replace("{placeholder}", "?");
796            prop_assert!(sql.contains(&expected));
797            prop_assert_eq!(params.len(), 1);
798        }
799
800        #[test]
801        fn prop_operator_lt_support(
802            field in field_name_strategy(),
803            value in sql_value_strategy()
804        ) {
805            let mut params = Vec::new();
806            let cond = Condition::Lt(field.clone(), value);
807            let sql = condition_to_sql(&cond, &mut params);
808
809            let expected = format!("{} < {{placeholder}}", field).replace("{placeholder}", "?");
810            prop_assert!(sql.contains(&expected));
811            prop_assert_eq!(params.len(), 1);
812        }
813
814        #[test]
815        fn prop_operator_gte_support(
816            field in field_name_strategy(),
817            value in sql_value_strategy()
818        ) {
819            let mut params = Vec::new();
820            let cond = Condition::Gte(field.clone(), value);
821            let sql = condition_to_sql(&cond, &mut params);
822
823            let expected = format!("{} >= {{placeholder}}", field).replace("{placeholder}", "?");
824            prop_assert!(sql.contains(&expected));
825            prop_assert_eq!(params.len(), 1);
826        }
827
828        #[test]
829        fn prop_operator_lte_support(
830            field in field_name_strategy(),
831            value in sql_value_strategy()
832        ) {
833            let mut params = Vec::new();
834            let cond = Condition::Lte(field.clone(), value);
835            let sql = condition_to_sql(&cond, &mut params);
836
837            let expected = format!("{} <= {{placeholder}}", field).replace("{placeholder}", "?");
838            prop_assert!(sql.contains(&expected));
839            prop_assert_eq!(params.len(), 1);
840        }
841
842        #[test]
843        fn prop_operator_in_support(
844            field in field_name_strategy(),
845            values in prop::collection::vec(sql_value_strategy(), 1..10)
846        ) {
847            let mut params = Vec::new();
848            let values_len = values.len();
849            let cond = Condition::In(field.clone(), values);
850            let sql = condition_to_sql(&cond, &mut params);
851
852            let expected = format!("{} IN", field);
853            prop_assert!(sql.contains(&expected));
854            prop_assert_eq!(params.len(), values_len);
855        }
856
857        #[test]
858        fn prop_operator_between_support(
859            field in field_name_strategy(),
860            start in sql_value_strategy(),
861            end in sql_value_strategy()
862        ) {
863            let mut params = Vec::new();
864            let cond = Condition::Between(field.clone(), start, end);
865            let sql = condition_to_sql(&cond, &mut params);
866
867            let expected = format!("{} BETWEEN {{p1}} AND {{p2}}", field)
868                .replace("{p1}", "?")
869                .replace("{p2}", "?");
870            prop_assert!(sql.contains(&expected));
871            prop_assert_eq!(params.len(), 2);
872        }
873
874        #[test]
875        fn prop_operator_like_support(
876            field in field_name_strategy(),
877            pattern in "[a-zA-Z0-9_%]{1,20}"
878        ) {
879            let mut params = Vec::new();
880            let cond = Condition::Like(field.clone(), pattern);
881            let sql = condition_to_sql(&cond, &mut params);
882
883            let expected = format!("{} LIKE {{placeholder}}", field).replace("{placeholder}", "?");
884            prop_assert!(sql.contains(&expected));
885            prop_assert_eq!(params.len(), 1);
886        }
887    }
888
889    // Feature: mysql-query-builder, Property 9: AND/OR 优先级处理
890    // 验证需求:3.7
891    proptest! {
892        #![proptest_config(ProptestConfig::with_cases(100))]
893
894        #[test]
895        fn prop_and_or_priority_handling(
896            field1 in field_name_strategy(),
897            field2 in field_name_strategy(),
898            value1 in sql_value_strategy(),
899            value2 in sql_value_strategy(),
900            value3 in sql_value_strategy()
901        ) {
902            let mut params = Vec::new();
903
904            // 构建 (field1 = value1 OR field1 = value2) AND field2 = value3
905            let cond = Condition::And(vec![
906                Condition::Or(vec![
907                    Condition::Eq(field1.clone(), value1),
908                    Condition::Eq(field1.clone(), value2),
909                ]),
910                Condition::Eq(field2.clone(), value3),
911            ]);
912
913            let sql = condition_to_sql(&cond, &mut params);
914
915            // 验证 SQL 有正确的括号确保操作符优先级
916            // 整个条件应该被括号包围
917            prop_assert!(sql.starts_with('('));
918            prop_assert!(sql.ends_with(')'));
919
920            // OR 条件应该被括号包围
921            prop_assert!(sql.contains(" OR "));
922            prop_assert!(sql.contains(" AND "));
923
924            // 参数数量应该正确
925            prop_assert_eq!(params.len(), 3);
926        }
927
928        #[test]
929        fn prop_nested_and_or_brackets(
930            field in field_name_strategy(),
931            values in prop::collection::vec(sql_value_strategy(), 2..5)
932        ) {
933            let mut params = Vec::new();
934
935            // 构建多个 OR 条件的 AND 组合
936            let or_conditions: Vec<Condition> = values
937                .iter()
938                .map(|v| Condition::Eq(field.clone(), v.clone()))
939                .collect();
940
941            let cond = Condition::And(vec![
942                Condition::Or(or_conditions.clone()),
943                Condition::Gt(field.clone(), SqlValue::Int(0)),
944            ]);
945
946            let sql = condition_to_sql(&cond, &mut params);
947
948            // 验证括号匹配
949            let open_count = sql.chars().filter(|&c| c == '(').count();
950            let close_count = sql.chars().filter(|&c| c == ')').count();
951            prop_assert_eq!(open_count, close_count);
952
953            // 验证参数数量
954            prop_assert_eq!(params.len(), values.len() + 1);
955        }
956
957        #[test]
958        fn prop_multiple_and_conditions(
959            field in field_name_strategy(),
960            values in prop::collection::vec(sql_value_strategy(), 2..5)
961        ) {
962            let mut params = Vec::new();
963
964            // 构建多个 AND 条件
965            let and_conditions: Vec<Condition> = values
966                .iter()
967                .enumerate()
968                .map(|(i, v)| {
969                    if i % 2 == 0 {
970                        Condition::Eq(field.clone(), v.clone())
971                    } else {
972                        Condition::Ne(field.clone(), v.clone())
973                    }
974                })
975                .collect();
976
977            let cond = Condition::And(and_conditions);
978            let sql = condition_to_sql(&cond, &mut params);
979
980            // 验证 AND 连接
981            let and_count = sql.matches(" AND ").count();
982            prop_assert_eq!(and_count, values.len() - 1);
983
984            // 验证参数数量
985            prop_assert_eq!(params.len(), values.len());
986        }
987
988        #[test]
989        fn prop_or_conditions_always_bracketed(
990            field in field_name_strategy(),
991            values in prop::collection::vec(sql_value_strategy(), 2..5)
992        ) {
993            let mut params = Vec::new();
994
995            // 构建 OR 条件
996            let or_conditions: Vec<Condition> = values
997                .iter()
998                .map(|v| Condition::Eq(field.clone(), v.clone()))
999                .collect();
1000
1001            let cond = Condition::Or(or_conditions);
1002            let sql = condition_to_sql(&cond, &mut params);
1003
1004            // OR 条件应该被括号包围
1005            prop_assert!(sql.starts_with('('));
1006            prop_assert!(sql.ends_with(')'));
1007
1008            // 验证 OR 连接
1009            let or_count = sql.matches(" OR ").count();
1010            prop_assert_eq!(or_count, values.len() - 1);
1011        }
1012    }
1013
1014    // **Validates: Requirements 5**
1015    //
1016    // 属性 P3:condition_to_sql_owned 与 condition_to_sql 等价性
1017    //
1018    // 形式化描述:对于任意 Condition c,
1019    // `condition_to_sql_owned(c.clone(), &mut p1)` 生成的 SQL 字符串
1020    // 与 `condition_to_sql(&c, &mut p2)` 完全相同,且参数列表长度相等。
1021    proptest! {
1022        #![proptest_config(ProptestConfig::with_cases(200))]
1023
1024        #[test]
1025        fn prop_owned_equals_borrowed_eq(
1026            field in field_name_strategy(),
1027            value in sql_value_strategy()
1028        ) {
1029            let cond = Condition::Eq(field, value);
1030            let mut p1 = vec![];
1031            let mut p2 = vec![];
1032            let sql1 = condition_to_sql_owned(cond.clone(), &mut p1);
1033            let sql2 = condition_to_sql(&cond, &mut p2);
1034            // SQL 字符串必须完全相同
1035            prop_assert_eq!(&sql1, &sql2);
1036            // 参数列表长度必须相等
1037            prop_assert_eq!(p1.len(), p2.len());
1038        }
1039
1040        #[test]
1041        fn prop_owned_equals_borrowed_ne(
1042            field in field_name_strategy(),
1043            value in sql_value_strategy()
1044        ) {
1045            let cond = Condition::Ne(field, value);
1046            let mut p1 = vec![];
1047            let mut p2 = vec![];
1048            let sql1 = condition_to_sql_owned(cond.clone(), &mut p1);
1049            let sql2 = condition_to_sql(&cond, &mut p2);
1050            prop_assert_eq!(&sql1, &sql2);
1051            prop_assert_eq!(p1.len(), p2.len());
1052        }
1053
1054        #[test]
1055        fn prop_owned_equals_borrowed_in(
1056            field in field_name_strategy(),
1057            values in prop::collection::vec(sql_value_strategy(), 0..8)
1058        ) {
1059            let cond = Condition::In(field, values);
1060            let mut p1 = vec![];
1061            let mut p2 = vec![];
1062            let sql1 = condition_to_sql_owned(cond.clone(), &mut p1);
1063            let sql2 = condition_to_sql(&cond, &mut p2);
1064            prop_assert_eq!(&sql1, &sql2);
1065            prop_assert_eq!(p1.len(), p2.len());
1066        }
1067
1068        #[test]
1069        fn prop_owned_equals_borrowed_between(
1070            field in field_name_strategy(),
1071            start in sql_value_strategy(),
1072            end in sql_value_strategy()
1073        ) {
1074            let cond = Condition::Between(field, start, end);
1075            let mut p1 = vec![];
1076            let mut p2 = vec![];
1077            let sql1 = condition_to_sql_owned(cond.clone(), &mut p1);
1078            let sql2 = condition_to_sql(&cond, &mut p2);
1079            prop_assert_eq!(&sql1, &sql2);
1080            prop_assert_eq!(p1.len(), p2.len());
1081        }
1082
1083        #[test]
1084        fn prop_owned_equals_borrowed_like(
1085            field in field_name_strategy(),
1086            pattern in "[a-zA-Z0-9_%]{1,20}"
1087        ) {
1088            let cond = Condition::Like(field, pattern);
1089            let mut p1 = vec![];
1090            let mut p2 = vec![];
1091            let sql1 = condition_to_sql_owned(cond.clone(), &mut p1);
1092            let sql2 = condition_to_sql(&cond, &mut p2);
1093            prop_assert_eq!(&sql1, &sql2);
1094            prop_assert_eq!(p1.len(), p2.len());
1095        }
1096
1097        #[test]
1098        fn prop_owned_equals_borrowed_and(
1099            field in field_name_strategy(),
1100            values in prop::collection::vec(sql_value_strategy(), 0..5)
1101        ) {
1102            // 构建 AND 条件
1103            let conditions: Vec<Condition> = values
1104                .iter()
1105                .map(|v| Condition::Eq(field.clone(), v.clone()))
1106                .collect();
1107            let cond = Condition::And(conditions);
1108            let mut p1 = vec![];
1109            let mut p2 = vec![];
1110            let sql1 = condition_to_sql_owned(cond.clone(), &mut p1);
1111            let sql2 = condition_to_sql(&cond, &mut p2);
1112            // SQL 字符串必须完全相同
1113            prop_assert_eq!(&sql1, &sql2);
1114            // 参数列表长度必须相等
1115            prop_assert_eq!(p1.len(), p2.len());
1116        }
1117
1118        #[test]
1119        fn prop_owned_equals_borrowed_or(
1120            field in field_name_strategy(),
1121            values in prop::collection::vec(sql_value_strategy(), 0..5)
1122        ) {
1123            // 构建 OR 条件
1124            let conditions: Vec<Condition> = values
1125                .iter()
1126                .map(|v| Condition::Eq(field.clone(), v.clone()))
1127                .collect();
1128            let cond = Condition::Or(conditions);
1129            let mut p1 = vec![];
1130            let mut p2 = vec![];
1131            let sql1 = condition_to_sql_owned(cond.clone(), &mut p1);
1132            let sql2 = condition_to_sql(&cond, &mut p2);
1133            // SQL 字符串必须完全相同
1134            prop_assert_eq!(&sql1, &sql2);
1135            // 参数列表长度必须相等
1136            prop_assert_eq!(p1.len(), p2.len());
1137        }
1138
1139        #[test]
1140        fn prop_owned_equals_borrowed_nested(
1141            field1 in field_name_strategy(),
1142            field2 in field_name_strategy(),
1143            values in prop::collection::vec(sql_value_strategy(), 2..4)
1144        ) {
1145            // 构建嵌套条件:(field1 = v1 OR field1 = v2) AND field2 = v3
1146            let or_conds: Vec<Condition> = values[..values.len()-1]
1147                .iter()
1148                .map(|v| Condition::Eq(field1.clone(), v.clone()))
1149                .collect();
1150            let cond = Condition::And(vec![
1151                Condition::Or(or_conds),
1152                Condition::Eq(field2.clone(), values.last().unwrap().clone()),
1153            ]);
1154            let mut p1 = vec![];
1155            let mut p2 = vec![];
1156            let sql1 = condition_to_sql_owned(cond.clone(), &mut p1);
1157            let sql2 = condition_to_sql(&cond, &mut p2);
1158            // SQL 字符串必须完全相同
1159            prop_assert_eq!(&sql1, &sql2);
1160            // 参数列表长度必须相等
1161            prop_assert_eq!(p1.len(), p2.len());
1162        }
1163    }
1164}