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 IsNull(String),
41 IsNotNull(String),
43 And(Vec<Condition>),
45 Or(Vec<Condition>),
47}
48
49impl 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
128pub 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 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 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 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
219pub fn condition_to_sql_owned(condition: Condition, params: &mut Vec<SqlValue>) -> String {
232 match condition {
233 Condition::Eq(field, value) => {
234 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 return "1 = 0".to_string();
262 }
263 let count = values.len();
264 params.extend(values);
266 let placeholders = vec!["?"; count].join(", ");
267 format!("{} IN ({})", field, placeholders)
268 }
269 Condition::Between(field, start, end) => {
270 params.push(start);
272 params.push(end);
273 format!("{} BETWEEN ? AND ?", field)
274 }
275 Condition::Like(field, pattern) => {
276 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 let mut iter = conditions.into_iter();
289 return condition_to_sql_owned(iter.next().unwrap(), params);
290 }
291 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 let mut iter = conditions.into_iter();
305 return condition_to_sql_owned(iter.next().unwrap(), params);
306 }
307 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]; 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 #[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 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 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 fn field_name_strategy() -> impl Strategy<Value = String> {
726 "[a-z][a-z0-9_]{0,30}"
727 }
728
729 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 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 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 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 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 prop_assert!(sql.starts_with('('));
918 prop_assert!(sql.ends_with(')'));
919
920 prop_assert!(sql.contains(" OR "));
922 prop_assert!(sql.contains(" AND "));
923
924 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 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 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 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 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 let and_count = sql.matches(" AND ").count();
982 prop_assert_eq!(and_count, values.len() - 1);
983
984 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 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 prop_assert!(sql.starts_with('('));
1006 prop_assert!(sql.ends_with(')'));
1007
1008 let or_count = sql.matches(" OR ").count();
1010 prop_assert_eq!(or_count, values.len() - 1);
1011 }
1012 }
1013
1014 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 prop_assert_eq!(&sql1, &sql2);
1036 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 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 prop_assert_eq!(&sql1, &sql2);
1114 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 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 prop_assert_eq!(&sql1, &sql2);
1135 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 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 prop_assert_eq!(&sql1, &sql2);
1160 prop_assert_eq!(p1.len(), p2.len());
1162 }
1163 }
1164}