1use chrono::NaiveDateTime;
2use serde_json::Value as JsonValue;
3
4#[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#[derive(Debug, Clone)]
20pub enum Condition {
21 Eq(String, SqlValue),
23 Ne(String, SqlValue),
25 Gt(String, SqlValue),
27 Lt(String, SqlValue),
29 Gte(String, SqlValue),
31 Lte(String, SqlValue),
33 In(String, Vec<SqlValue>),
35 Between(String, SqlValue, SqlValue),
37 Like(String, String),
39 And(Vec<Condition>),
41 Or(Vec<Condition>),
43}
44
45impl From<i32> for SqlValue {
47 fn from(v: i32) -> Self {
48 SqlValue::Int(v as i64)
49 }
50}
51
52impl From<i64> for SqlValue {
53 fn from(v: i64) -> Self {
54 SqlValue::Int(v)
55 }
56}
57
58impl From<u64> for SqlValue {
59 fn from(v: u64) -> Self {
60 SqlValue::Int(v as i64)
61 }
62}
63
64impl From<f64> for SqlValue {
65 fn from(v: f64) -> Self {
66 SqlValue::Float(v)
67 }
68}
69
70impl From<f32> for SqlValue {
71 fn from(v: f32) -> Self {
72 SqlValue::Float(v as f64)
73 }
74}
75
76impl From<String> for SqlValue {
77 fn from(v: String) -> Self {
78 SqlValue::String(v)
79 }
80}
81
82impl From<&str> for SqlValue {
83 fn from(v: &str) -> Self {
84 SqlValue::String(v.to_string())
85 }
86}
87
88impl From<bool> for SqlValue {
89 fn from(v: bool) -> Self {
90 SqlValue::Bool(v)
91 }
92}
93
94impl From<Vec<u8>> for SqlValue {
95 fn from(v: Vec<u8>) -> Self {
96 SqlValue::Bytes(v)
97 }
98}
99
100impl From<JsonValue> for SqlValue {
101 fn from(v: JsonValue) -> Self {
102 SqlValue::Json(v)
103 }
104}
105
106impl From<NaiveDateTime> for SqlValue {
107 fn from(v: NaiveDateTime) -> Self {
108 SqlValue::DateTime(v)
109 }
110}
111
112impl<T> From<Option<T>> for SqlValue
113where
114 T: Into<SqlValue>,
115{
116 fn from(v: Option<T>) -> Self {
117 match v {
118 Some(val) => val.into(),
119 None => SqlValue::Null,
120 }
121 }
122}
123
124pub fn condition_to_sql(condition: &Condition, params: &mut Vec<SqlValue>) -> String {
133 match condition {
134 Condition::Eq(field, value) => {
135 params.push(value.clone());
136 format!("{} = ?", field)
137 }
138 Condition::Ne(field, value) => {
139 params.push(value.clone());
140 format!("{} != ?", field)
141 }
142 Condition::Gt(field, value) => {
143 params.push(value.clone());
144 format!("{} > ?", field)
145 }
146 Condition::Lt(field, value) => {
147 params.push(value.clone());
148 format!("{} < ?", field)
149 }
150 Condition::Gte(field, value) => {
151 params.push(value.clone());
152 format!("{} >= ?", field)
153 }
154 Condition::Lte(field, value) => {
155 params.push(value.clone());
156 format!("{} <= ?", field)
157 }
158 Condition::In(field, values) => {
159 if values.is_empty() {
160 return "1 = 0".to_string();
162 }
163 let placeholders = values
164 .iter()
165 .map(|v| {
166 params.push(v.clone());
167 "?"
168 })
169 .collect::<Vec<_>>()
170 .join(", ");
171 format!("{} IN ({})", field, placeholders)
172 }
173 Condition::Between(field, start, end) => {
174 params.push(start.clone());
175 params.push(end.clone());
176 format!("{} BETWEEN ? AND ?", field)
177 }
178 Condition::Like(field, pattern) => {
179 params.push(SqlValue::String(pattern.clone()));
180 format!("{} LIKE ?", field)
181 }
182 Condition::And(conditions) => {
183 if conditions.is_empty() {
184 return "1 = 1".to_string();
185 }
186 if conditions.len() == 1 {
187 return condition_to_sql(&conditions[0], params);
188 }
189 let parts: Vec<String> = conditions
191 .iter()
192 .map(|c| condition_to_sql(c, params))
193 .collect();
194 format!("({})", parts.join(" AND "))
195 }
196 Condition::Or(conditions) => {
197 if conditions.is_empty() {
198 return "1 = 0".to_string();
199 }
200 if conditions.len() == 1 {
201 return condition_to_sql(&conditions[0], params);
202 }
203 let parts: Vec<String> = conditions
205 .iter()
206 .map(|c| condition_to_sql(c, params))
207 .collect();
208 format!("({})", parts.join(" OR "))
209 }
210 }
211}
212
213#[cfg(test)]
214mod tests {
215 use super::*;
216 use chrono::NaiveDate;
217
218 #[test]
219 fn test_from_i32() {
220 let value: SqlValue = 42i32.into();
221 match value {
222 SqlValue::Int(v) => assert_eq!(v, 42),
223 _ => panic!("期望 SqlValue::Int"),
224 }
225 }
226
227 #[test]
228 fn test_from_i64() {
229 let value: SqlValue = 9223372036854775807i64.into();
230 match value {
231 SqlValue::Int(v) => assert_eq!(v, 9223372036854775807),
232 _ => panic!("期望 SqlValue::Int"),
233 }
234 }
235
236 #[test]
237 fn test_from_f32() {
238 let value: SqlValue = 3.5f32.into();
239 match value {
240 SqlValue::Float(v) => assert!((v - 3.5).abs() < 0.01),
241 _ => panic!("期望 SqlValue::Float"),
242 }
243 }
244
245 #[test]
246 fn test_from_f64() {
247 let value: SqlValue = 2.5f64.into();
248 match value {
249 SqlValue::Float(v) => assert!((v - 2.5).abs() < 0.000001),
250 _ => panic!("期望 SqlValue::Float"),
251 }
252 }
253
254 #[test]
255 fn test_from_string() {
256 let value: SqlValue = String::from("测试字符串").into();
257 match value {
258 SqlValue::String(s) => assert_eq!(s, "测试字符串"),
259 _ => panic!("期望 SqlValue::String"),
260 }
261 }
262
263 #[test]
264 fn test_from_str() {
265 let value: SqlValue = "hello world".into();
266 match value {
267 SqlValue::String(s) => assert_eq!(s, "hello world"),
268 _ => panic!("期望 SqlValue::String"),
269 }
270 }
271
272 #[test]
273 fn test_from_bool_true() {
274 let value: SqlValue = true.into();
275 match value {
276 SqlValue::Bool(b) => assert!(b),
277 _ => panic!("期望 SqlValue::Bool"),
278 }
279 }
280
281 #[test]
282 fn test_from_bool_false() {
283 let value: SqlValue = false.into();
284 match value {
285 SqlValue::Bool(b) => assert!(!b),
286 _ => panic!("期望 SqlValue::Bool"),
287 }
288 }
289
290 #[test]
291 fn test_from_vec_u8() {
292 let bytes = vec![0x48, 0x65, 0x6c, 0x6c, 0x6f]; let value: SqlValue = bytes.clone().into();
294 match value {
295 SqlValue::Bytes(b) => assert_eq!(b, bytes),
296 _ => panic!("期望 SqlValue::Bytes"),
297 }
298 }
299
300 #[test]
301 fn test_from_json_value() {
302 let json = serde_json::json!({
303 "name": "测试",
304 "age": 25,
305 "active": true
306 });
307 let value: SqlValue = json.clone().into();
308 match value {
309 SqlValue::Json(j) => assert_eq!(j, json),
310 _ => panic!("期望 SqlValue::Json"),
311 }
312 }
313
314 #[test]
315 fn test_from_naive_datetime() {
316 let dt = NaiveDate::from_ymd_opt(2024, 1, 15)
317 .unwrap()
318 .and_hms_opt(10, 30, 45)
319 .unwrap();
320 let value: SqlValue = dt.into();
321 match value {
322 SqlValue::DateTime(d) => assert_eq!(d, dt),
323 _ => panic!("期望 SqlValue::DateTime"),
324 }
325 }
326
327 #[test]
328 fn test_from_option_some() {
329 let value: SqlValue = Some(42i32).into();
330 match value {
331 SqlValue::Int(v) => assert_eq!(v, 42),
332 _ => panic!("期望 SqlValue::Int"),
333 }
334 }
335
336 #[test]
337 fn test_from_option_none() {
338 let value: SqlValue = None::<i32>.into();
339 match value {
340 SqlValue::Null => (),
341 _ => panic!("期望 SqlValue::Null"),
342 }
343 }
344
345 #[test]
346 fn test_from_option_string_some() {
347 let value: SqlValue = Some(String::from("测试")).into();
348 match value {
349 SqlValue::String(s) => assert_eq!(s, "测试"),
350 _ => panic!("期望 SqlValue::String"),
351 }
352 }
353
354 #[test]
355 fn test_from_option_string_none() {
356 let value: SqlValue = None::<String>.into();
357 match value {
358 SqlValue::Null => (),
359 _ => panic!("期望 SqlValue::Null"),
360 }
361 }
362
363 #[test]
364 fn test_negative_integers() {
365 let value: SqlValue = (-100i32).into();
366 match value {
367 SqlValue::Int(v) => assert_eq!(v, -100),
368 _ => panic!("期望 SqlValue::Int"),
369 }
370 }
371
372 #[test]
373 fn test_negative_floats() {
374 let value: SqlValue = (-3.5f64).into();
375 match value {
376 SqlValue::Float(v) => assert!((v + 3.5).abs() < 0.01),
377 _ => panic!("期望 SqlValue::Float"),
378 }
379 }
380
381 #[test]
382 fn test_empty_string() {
383 let value: SqlValue = "".into();
384 match value {
385 SqlValue::String(s) => assert_eq!(s, ""),
386 _ => panic!("期望 SqlValue::String"),
387 }
388 }
389
390 #[test]
391 fn test_empty_bytes() {
392 let value: SqlValue = Vec::<u8>::new().into();
393 match value {
394 SqlValue::Bytes(b) => assert!(b.is_empty()),
395 _ => panic!("期望 SqlValue::Bytes"),
396 }
397 }
398
399 #[test]
400 fn test_json_null() {
401 let json = serde_json::Value::Null;
402 let value: SqlValue = json.into();
403 match value {
404 SqlValue::Json(j) => assert!(j.is_null()),
405 _ => panic!("期望 SqlValue::Json"),
406 }
407 }
408
409 #[test]
410 fn test_json_array() {
411 let json = serde_json::json!([1, 2, 3, 4, 5]);
412 let value: SqlValue = json.clone().into();
413 match value {
414 SqlValue::Json(j) => assert_eq!(j, json),
415 _ => panic!("期望 SqlValue::Json"),
416 }
417 }
418
419 #[test]
420 fn test_unicode_string() {
421 let value: SqlValue = "你好世界 🌍".into();
422 match value {
423 SqlValue::String(s) => assert_eq!(s, "你好世界 🌍"),
424 _ => panic!("期望 SqlValue::String"),
425 }
426 }
427
428 #[test]
429 fn test_zero_values() {
430 let int_value: SqlValue = 0i32.into();
431 match int_value {
432 SqlValue::Int(v) => assert_eq!(v, 0),
433 _ => panic!("期望 SqlValue::Int"),
434 }
435
436 let float_value: SqlValue = 0.0f64.into();
437 match float_value {
438 SqlValue::Float(v) => assert_eq!(v, 0.0),
439 _ => panic!("期望 SqlValue::Float"),
440 }
441 }
442
443 #[test]
445 fn test_condition_eq() {
446 let mut params = Vec::new();
447 let cond = Condition::Eq("name".to_string(), SqlValue::String("test".to_string()));
448 let sql = super::condition_to_sql(&cond, &mut params);
449 assert_eq!(sql, "name = ?");
450 assert_eq!(params.len(), 1);
451 }
452
453 #[test]
454 fn test_condition_ne() {
455 let mut params = Vec::new();
456 let cond = Condition::Ne("status".to_string(), SqlValue::Int(1));
457 let sql = super::condition_to_sql(&cond, &mut params);
458 assert_eq!(sql, "status != ?");
459 assert_eq!(params.len(), 1);
460 }
461
462 #[test]
463 fn test_condition_gt() {
464 let mut params = Vec::new();
465 let cond = Condition::Gt("age".to_string(), SqlValue::Int(18));
466 let sql = super::condition_to_sql(&cond, &mut params);
467 assert_eq!(sql, "age > ?");
468 assert_eq!(params.len(), 1);
469 }
470
471 #[test]
472 fn test_condition_lt() {
473 let mut params = Vec::new();
474 let cond = Condition::Lt("price".to_string(), SqlValue::Float(100.0));
475 let sql = super::condition_to_sql(&cond, &mut params);
476 assert_eq!(sql, "price < ?");
477 assert_eq!(params.len(), 1);
478 }
479
480 #[test]
481 fn test_condition_gte() {
482 let mut params = Vec::new();
483 let cond = Condition::Gte("score".to_string(), SqlValue::Int(60));
484 let sql = super::condition_to_sql(&cond, &mut params);
485 assert_eq!(sql, "score >= ?");
486 assert_eq!(params.len(), 1);
487 }
488
489 #[test]
490 fn test_condition_lte() {
491 let mut params = Vec::new();
492 let cond = Condition::Lte("count".to_string(), SqlValue::Int(10));
493 let sql = super::condition_to_sql(&cond, &mut params);
494 assert_eq!(sql, "count <= ?");
495 assert_eq!(params.len(), 1);
496 }
497
498 #[test]
499 fn test_condition_in() {
500 let mut params = Vec::new();
501 let cond = Condition::In(
502 "id".to_string(),
503 vec![SqlValue::Int(1), SqlValue::Int(2), SqlValue::Int(3)],
504 );
505 let sql = super::condition_to_sql(&cond, &mut params);
506 assert_eq!(sql, "id IN (?, ?, ?)");
507 assert_eq!(params.len(), 3);
508 }
509
510 #[test]
511 fn test_condition_in_empty() {
512 let mut params = Vec::new();
513 let cond = Condition::In("id".to_string(), vec![]);
514 let sql = super::condition_to_sql(&cond, &mut params);
515 assert_eq!(sql, "1 = 0");
516 assert_eq!(params.len(), 0);
517 }
518
519 #[test]
520 fn test_condition_between() {
521 let mut params = Vec::new();
522 let cond = Condition::Between("age".to_string(), SqlValue::Int(18), SqlValue::Int(65));
523 let sql = super::condition_to_sql(&cond, &mut params);
524 assert_eq!(sql, "age BETWEEN ? AND ?");
525 assert_eq!(params.len(), 2);
526 }
527
528 #[test]
529 fn test_condition_like() {
530 let mut params = Vec::new();
531 let cond = Condition::Like("name".to_string(), "%test%".to_string());
532 let sql = super::condition_to_sql(&cond, &mut params);
533 assert_eq!(sql, "name LIKE ?");
534 assert_eq!(params.len(), 1);
535 }
536
537 #[test]
538 fn test_condition_and() {
539 let mut params = Vec::new();
540 let cond = Condition::And(vec![
541 Condition::Eq("name".to_string(), SqlValue::String("test".to_string())),
542 Condition::Gt("age".to_string(), SqlValue::Int(18)),
543 ]);
544 let sql = super::condition_to_sql(&cond, &mut params);
545 assert_eq!(sql, "(name = ? AND age > ?)");
546 assert_eq!(params.len(), 2);
547 }
548
549 #[test]
550 fn test_condition_or() {
551 let mut params = Vec::new();
552 let cond = Condition::Or(vec![
553 Condition::Eq("status".to_string(), SqlValue::Int(1)),
554 Condition::Eq("status".to_string(), SqlValue::Int(2)),
555 ]);
556 let sql = super::condition_to_sql(&cond, &mut params);
557 assert_eq!(sql, "(status = ? OR status = ?)");
558 assert_eq!(params.len(), 2);
559 }
560
561 #[test]
562 fn test_condition_and_or_priority() {
563 let mut params = Vec::new();
564 let cond = Condition::And(vec![
566 Condition::Or(vec![
567 Condition::Eq("name".to_string(), SqlValue::String("test".to_string())),
568 Condition::Eq("name".to_string(), SqlValue::String("demo".to_string())),
569 ]),
570 Condition::Gt("age".to_string(), SqlValue::Int(18)),
571 ]);
572 let sql = super::condition_to_sql(&cond, &mut params);
573 assert_eq!(sql, "((name = ? OR name = ?) AND age > ?)");
575 assert_eq!(params.len(), 3);
576 }
577
578 #[test]
579 fn test_condition_empty_and() {
580 let mut params = Vec::new();
581 let cond = Condition::And(vec![]);
582 let sql = super::condition_to_sql(&cond, &mut params);
583 assert_eq!(sql, "1 = 1");
584 assert_eq!(params.len(), 0);
585 }
586
587 #[test]
588 fn test_condition_empty_or() {
589 let mut params = Vec::new();
590 let cond = Condition::Or(vec![]);
591 let sql = super::condition_to_sql(&cond, &mut params);
592 assert_eq!(sql, "1 = 0");
593 assert_eq!(params.len(), 0);
594 }
595
596 #[test]
597 fn test_condition_single_and() {
598 let mut params = Vec::new();
599 let cond = Condition::And(vec![Condition::Eq("id".to_string(), SqlValue::Int(1))]);
600 let sql = super::condition_to_sql(&cond, &mut params);
601 assert_eq!(sql, "id = ?");
602 assert_eq!(params.len(), 1);
603 }
604
605 #[test]
606 fn test_condition_single_or() {
607 let mut params = Vec::new();
608 let cond = Condition::Or(vec![Condition::Eq("id".to_string(), SqlValue::Int(1))]);
609 let sql = super::condition_to_sql(&cond, &mut params);
610 assert_eq!(sql, "id = ?");
611 assert_eq!(params.len(), 1);
612 }
613}
614
615#[cfg(test)]
616mod property_tests {
617 use super::*;
618 use proptest::prelude::*;
619
620 fn field_name_strategy() -> impl Strategy<Value = String> {
622 "[a-z][a-z0-9_]{0,30}"
623 }
624
625 fn sql_value_strategy() -> impl Strategy<Value = SqlValue> {
627 prop_oneof![
628 Just(SqlValue::Null),
629 any::<bool>().prop_map(SqlValue::Bool),
630 any::<i64>().prop_map(SqlValue::Int),
631 any::<f64>().prop_map(|f| {
632 if f.is_finite() {
633 SqlValue::Float(f)
634 } else {
635 SqlValue::Float(0.0)
636 }
637 }),
638 "[a-zA-Z0-9_\\s]{0,50}".prop_map(SqlValue::String),
639 ]
640 }
641
642 proptest! {
651 #![proptest_config(ProptestConfig::with_cases(100))]
652
653 #[test]
654 fn prop_operator_eq_support(
655 field in field_name_strategy(),
656 value in sql_value_strategy()
657 ) {
658 let mut params = Vec::new();
659 let cond = Condition::Eq(field.clone(), value);
660 let sql = condition_to_sql(&cond, &mut params);
661
662 let expected = format!("{} = {{placeholder}}", field).replace("{placeholder}", "?");
664 prop_assert!(sql.contains(&expected));
665 prop_assert_eq!(params.len(), 1);
666 }
667
668 #[test]
669 fn prop_operator_ne_support(
670 field in field_name_strategy(),
671 value in sql_value_strategy()
672 ) {
673 let mut params = Vec::new();
674 let cond = Condition::Ne(field.clone(), value);
675 let sql = condition_to_sql(&cond, &mut params);
676
677 let expected = format!("{} != {{placeholder}}", field).replace("{placeholder}", "?");
678 prop_assert!(sql.contains(&expected));
679 prop_assert_eq!(params.len(), 1);
680 }
681
682 #[test]
683 fn prop_operator_gt_support(
684 field in field_name_strategy(),
685 value in sql_value_strategy()
686 ) {
687 let mut params = Vec::new();
688 let cond = Condition::Gt(field.clone(), value);
689 let sql = condition_to_sql(&cond, &mut params);
690
691 let expected = format!("{} > {{placeholder}}", field).replace("{placeholder}", "?");
692 prop_assert!(sql.contains(&expected));
693 prop_assert_eq!(params.len(), 1);
694 }
695
696 #[test]
697 fn prop_operator_lt_support(
698 field in field_name_strategy(),
699 value in sql_value_strategy()
700 ) {
701 let mut params = Vec::new();
702 let cond = Condition::Lt(field.clone(), value);
703 let sql = condition_to_sql(&cond, &mut params);
704
705 let expected = format!("{} < {{placeholder}}", field).replace("{placeholder}", "?");
706 prop_assert!(sql.contains(&expected));
707 prop_assert_eq!(params.len(), 1);
708 }
709
710 #[test]
711 fn prop_operator_gte_support(
712 field in field_name_strategy(),
713 value in sql_value_strategy()
714 ) {
715 let mut params = Vec::new();
716 let cond = Condition::Gte(field.clone(), value);
717 let sql = condition_to_sql(&cond, &mut params);
718
719 let expected = format!("{} >= {{placeholder}}", field).replace("{placeholder}", "?");
720 prop_assert!(sql.contains(&expected));
721 prop_assert_eq!(params.len(), 1);
722 }
723
724 #[test]
725 fn prop_operator_lte_support(
726 field in field_name_strategy(),
727 value in sql_value_strategy()
728 ) {
729 let mut params = Vec::new();
730 let cond = Condition::Lte(field.clone(), value);
731 let sql = condition_to_sql(&cond, &mut params);
732
733 let expected = format!("{} <= {{placeholder}}", field).replace("{placeholder}", "?");
734 prop_assert!(sql.contains(&expected));
735 prop_assert_eq!(params.len(), 1);
736 }
737
738 #[test]
739 fn prop_operator_in_support(
740 field in field_name_strategy(),
741 values in prop::collection::vec(sql_value_strategy(), 1..10)
742 ) {
743 let mut params = Vec::new();
744 let values_len = values.len();
745 let cond = Condition::In(field.clone(), values);
746 let sql = condition_to_sql(&cond, &mut params);
747
748 let expected = format!("{} IN", field);
749 prop_assert!(sql.contains(&expected));
750 prop_assert_eq!(params.len(), values_len);
751 }
752
753 #[test]
754 fn prop_operator_between_support(
755 field in field_name_strategy(),
756 start in sql_value_strategy(),
757 end in sql_value_strategy()
758 ) {
759 let mut params = Vec::new();
760 let cond = Condition::Between(field.clone(), start, end);
761 let sql = condition_to_sql(&cond, &mut params);
762
763 let expected = format!("{} BETWEEN {{p1}} AND {{p2}}", field)
764 .replace("{p1}", "?")
765 .replace("{p2}", "?");
766 prop_assert!(sql.contains(&expected));
767 prop_assert_eq!(params.len(), 2);
768 }
769
770 #[test]
771 fn prop_operator_like_support(
772 field in field_name_strategy(),
773 pattern in "[a-zA-Z0-9_%]{1,20}"
774 ) {
775 let mut params = Vec::new();
776 let cond = Condition::Like(field.clone(), pattern);
777 let sql = condition_to_sql(&cond, &mut params);
778
779 let expected = format!("{} LIKE {{placeholder}}", field).replace("{placeholder}", "?");
780 prop_assert!(sql.contains(&expected));
781 prop_assert_eq!(params.len(), 1);
782 }
783 }
784
785 proptest! {
788 #![proptest_config(ProptestConfig::with_cases(100))]
789
790 #[test]
791 fn prop_and_or_priority_handling(
792 field1 in field_name_strategy(),
793 field2 in field_name_strategy(),
794 value1 in sql_value_strategy(),
795 value2 in sql_value_strategy(),
796 value3 in sql_value_strategy()
797 ) {
798 let mut params = Vec::new();
799
800 let cond = Condition::And(vec![
802 Condition::Or(vec![
803 Condition::Eq(field1.clone(), value1),
804 Condition::Eq(field1.clone(), value2),
805 ]),
806 Condition::Eq(field2.clone(), value3),
807 ]);
808
809 let sql = condition_to_sql(&cond, &mut params);
810
811 prop_assert!(sql.starts_with('('));
814 prop_assert!(sql.ends_with(')'));
815
816 prop_assert!(sql.contains(" OR "));
818 prop_assert!(sql.contains(" AND "));
819
820 prop_assert_eq!(params.len(), 3);
822 }
823
824 #[test]
825 fn prop_nested_and_or_brackets(
826 field in field_name_strategy(),
827 values in prop::collection::vec(sql_value_strategy(), 2..5)
828 ) {
829 let mut params = Vec::new();
830
831 let or_conditions: Vec<Condition> = values
833 .iter()
834 .map(|v| Condition::Eq(field.clone(), v.clone()))
835 .collect();
836
837 let cond = Condition::And(vec![
838 Condition::Or(or_conditions.clone()),
839 Condition::Gt(field.clone(), SqlValue::Int(0)),
840 ]);
841
842 let sql = condition_to_sql(&cond, &mut params);
843
844 let open_count = sql.chars().filter(|&c| c == '(').count();
846 let close_count = sql.chars().filter(|&c| c == ')').count();
847 prop_assert_eq!(open_count, close_count);
848
849 prop_assert_eq!(params.len(), values.len() + 1);
851 }
852
853 #[test]
854 fn prop_multiple_and_conditions(
855 field in field_name_strategy(),
856 values in prop::collection::vec(sql_value_strategy(), 2..5)
857 ) {
858 let mut params = Vec::new();
859
860 let and_conditions: Vec<Condition> = values
862 .iter()
863 .enumerate()
864 .map(|(i, v)| {
865 if i % 2 == 0 {
866 Condition::Eq(field.clone(), v.clone())
867 } else {
868 Condition::Ne(field.clone(), v.clone())
869 }
870 })
871 .collect();
872
873 let cond = Condition::And(and_conditions);
874 let sql = condition_to_sql(&cond, &mut params);
875
876 let and_count = sql.matches(" AND ").count();
878 prop_assert_eq!(and_count, values.len() - 1);
879
880 prop_assert_eq!(params.len(), values.len());
882 }
883
884 #[test]
885 fn prop_or_conditions_always_bracketed(
886 field in field_name_strategy(),
887 values in prop::collection::vec(sql_value_strategy(), 2..5)
888 ) {
889 let mut params = Vec::new();
890
891 let or_conditions: Vec<Condition> = values
893 .iter()
894 .map(|v| Condition::Eq(field.clone(), v.clone()))
895 .collect();
896
897 let cond = Condition::Or(or_conditions);
898 let sql = condition_to_sql(&cond, &mut params);
899
900 prop_assert!(sql.starts_with('('));
902 prop_assert!(sql.ends_with(')'));
903
904 let or_count = sql.matches(" OR ").count();
906 prop_assert_eq!(or_count, values.len() - 1);
907 }
908 }
909}