1use serde_json::Value;
102use std::collections::HashMap;
103
104pub fn has_many_in_sql(child_table: &str, foreign_key: &str, parent_pk_values: &[&str]) -> String {
140 if parent_pk_values.is_empty() {
141 return format!(
142 "SELECT * FROM {} WHERE {} IN (NULL)",
143 child_table, foreign_key
144 );
145 }
146 format!(
147 "SELECT * FROM {} WHERE {} IN ({})",
148 child_table,
149 foreign_key,
150 parent_pk_values.join(", ")
151 )
152}
153
154pub fn has_one_in_sql(child_table: &str, foreign_key: &str, parent_pk_values: &[&str]) -> String {
169 has_many_in_sql(child_table, foreign_key, parent_pk_values)
170}
171
172pub fn belongs_to_in_sql(parent_table: &str, parent_pk: &str, fk_values: &[&str]) -> String {
192 if fk_values.is_empty() {
193 return format!(
194 "SELECT * FROM {} WHERE {} IN (NULL)",
195 parent_table, parent_pk
196 );
197 }
198 format!(
199 "SELECT * FROM {} WHERE {} IN ({})",
200 parent_table,
201 parent_pk,
202 fk_values.join(", ")
203 )
204}
205
206pub fn belongs_to_many_in_sql(
236 target_table: &str,
237 junction_table: &str,
238 target_pk: &str,
239 other_key: &str,
240 foreign_key: &str,
241 current_pk_values: &[&str],
242) -> String {
243 if current_pk_values.is_empty() {
244 return format!(
245 "SELECT t.* FROM {} t INNER JOIN {} j ON t.{} = j.{} WHERE j.{} IN (NULL)",
246 target_table, junction_table, target_pk, other_key, foreign_key
247 );
248 }
249 format!(
250 "SELECT t.* FROM {} t INNER JOIN {} j ON t.{} = j.{} WHERE j.{} IN ({})",
251 target_table,
252 junction_table,
253 target_pk,
254 other_key,
255 foreign_key,
256 current_pk_values.join(", ")
257 )
258}
259
260pub fn sanitize_pk_value(value: &str) -> String {
278 if value.parse::<i64>().is_ok() || value.parse::<f64>().is_ok() {
280 value.to_string()
281 } else {
282 format!("'{}'", value.replace('\'', "''"))
284 }
285}
286
287pub fn collect_pk_values(rows: &[Value], pk_field: &str) -> Vec<String> {
328 rows.iter()
329 .filter_map(|row| {
330 row.get(pk_field)
331 .filter(|v| !v.is_null())
332 .map(value_to_pk_string)
333 })
334 .collect()
335}
336
337pub fn group_by_fk(rows: Vec<Value>, fk_field: &str) -> HashMap<String, Vec<Value>> {
378 let mut grouped: HashMap<String, Vec<Value>> = HashMap::new();
379 for row in rows {
380 let fk_value = row
381 .get(fk_field)
382 .filter(|v| !v.is_null())
383 .map(value_to_pk_string)
384 .unwrap_or_default();
385 grouped.entry(fk_value).or_default().push(row);
386 }
387 grouped
388}
389
390pub fn parse_with_notation(with: &str) -> (&str, Option<&str>) {
423 match with.split_once('.') {
424 Some((relation, sub)) => (relation, Some(sub)),
425 None => (with, None),
426 }
427}
428
429fn value_to_pk_string(value: &Value) -> String {
440 match value {
441 Value::Number(n) => n.to_string(),
442 Value::String(s) => s.clone(),
443 Value::Bool(b) => b.to_string(),
444 _ => String::new(),
445 }
446}
447
448#[cfg(test)]
453mod tests {
454 use super::*;
455 use serde_json::json;
456
457 #[test]
462 fn test_has_many_in_sql_multiple_pks() {
463 let sql = has_many_in_sql("orders", "user_id", &["1", "2", "3"]);
465 assert_eq!(sql, "SELECT * FROM orders WHERE user_id IN (1, 2, 3)");
466 }
467
468 #[test]
469 fn test_has_many_in_sql_single_pk() {
470 let sql = has_many_in_sql("orders", "user_id", &["1"]);
471 assert_eq!(sql, "SELECT * FROM orders WHERE user_id IN (1)");
472 }
473
474 #[test]
475 fn test_has_many_in_sql_empty_pks() {
476 let sql = has_many_in_sql("orders", "user_id", &[]);
478 assert_eq!(sql, "SELECT * FROM orders WHERE user_id IN (NULL)");
479 }
480
481 #[test]
482 fn test_has_many_in_sql_string_pks() {
483 let sql = has_many_in_sql("orders", "user_id", &["'abc-1'", "'abc-2'"]);
485 assert_eq!(
486 sql,
487 "SELECT * FROM orders WHERE user_id IN ('abc-1', 'abc-2')"
488 );
489 }
490
491 #[test]
492 fn test_has_many_in_sql_custom_foreign_key() {
493 let sql = has_many_in_sql("orders", "uid", &["1", "2"]);
494 assert_eq!(sql, "SELECT * FROM orders WHERE uid IN (1, 2)");
495 }
496
497 #[test]
498 fn test_has_many_in_sql_multi_word_table() {
499 let sql = has_many_in_sql("order_items", "order_id", &["1", "2"]);
500 assert_eq!(sql, "SELECT * FROM order_items WHERE order_id IN (1, 2)");
501 }
502
503 #[test]
508 fn test_has_one_in_sql_multiple_pks() {
509 let sql = has_one_in_sql("profiles", "user_id", &["1", "2", "3"]);
511 assert_eq!(sql, "SELECT * FROM profiles WHERE user_id IN (1, 2, 3)");
512 }
513
514 #[test]
515 fn test_has_one_in_sql_empty_pks() {
516 let sql = has_one_in_sql("profiles", "user_id", &[]);
517 assert_eq!(sql, "SELECT * FROM profiles WHERE user_id IN (NULL)");
518 }
519
520 #[test]
521 fn test_has_one_in_sql_equals_has_many_in_sql() {
522 let has_one = has_one_in_sql("profiles", "user_id", &["1", "2"]);
524 let has_many = has_many_in_sql("profiles", "user_id", &["1", "2"]);
525 assert_eq!(has_one, has_many);
526 }
527
528 #[test]
533 fn test_belongs_to_in_sql_multiple_fks() {
534 let sql = belongs_to_in_sql("depts", "id", &["1", "2", "3"]);
536 assert_eq!(sql, "SELECT * FROM depts WHERE id IN (1, 2, 3)");
537 }
538
539 #[test]
540 fn test_belongs_to_in_sql_single_fk() {
541 let sql = belongs_to_in_sql("depts", "id", &["1"]);
542 assert_eq!(sql, "SELECT * FROM depts WHERE id IN (1)");
543 }
544
545 #[test]
546 fn test_belongs_to_in_sql_empty_fks() {
547 let sql = belongs_to_in_sql("depts", "id", &[]);
548 assert_eq!(sql, "SELECT * FROM depts WHERE id IN (NULL)");
549 }
550
551 #[test]
552 fn test_belongs_to_in_sql_custom_parent_pk() {
553 let sql = belongs_to_in_sql("categories", "cid", &["1", "2"]);
555 assert_eq!(sql, "SELECT * FROM categories WHERE cid IN (1, 2)");
556 }
557
558 #[test]
559 fn test_belongs_to_in_sql_multi_word_table() {
560 let sql = belongs_to_in_sql("user_profiles", "id", &["1", "2"]);
561 assert_eq!(sql, "SELECT * FROM user_profiles WHERE id IN (1, 2)");
562 }
563
564 #[test]
569 fn test_belongs_to_many_in_sql_multiple_pks() {
570 let sql = belongs_to_many_in_sql(
571 "roles",
572 "user_role",
573 "id",
574 "role_id",
575 "user_id",
576 &["1", "2", "3"],
577 );
578 assert_eq!(
579 sql,
580 "SELECT t.* FROM roles t INNER JOIN user_role j ON t.id = j.role_id WHERE j.user_id IN (1, 2, 3)"
581 );
582 }
583
584 #[test]
585 fn test_belongs_to_many_in_sql_single_pk() {
586 let sql = belongs_to_many_in_sql("roles", "user_role", "id", "role_id", "user_id", &["1"]);
587 assert_eq!(
588 sql,
589 "SELECT t.* FROM roles t INNER JOIN user_role j ON t.id = j.role_id WHERE j.user_id IN (1)"
590 );
591 }
592
593 #[test]
594 fn test_belongs_to_many_in_sql_empty_pks() {
595 let sql = belongs_to_many_in_sql("roles", "user_role", "id", "role_id", "user_id", &[]);
596 assert_eq!(
597 sql,
598 "SELECT t.* FROM roles t INNER JOIN user_role j ON t.id = j.role_id WHERE j.user_id IN (NULL)"
599 );
600 }
601
602 #[test]
603 fn test_belongs_to_many_in_sql_custom_keys() {
604 let sql = belongs_to_many_in_sql("roles", "user_role", "pk", "rid", "uid", &["1", "2"]);
606 assert_eq!(
607 sql,
608 "SELECT t.* FROM roles t INNER JOIN user_role j ON t.pk = j.rid WHERE j.uid IN (1, 2)"
609 );
610 }
611
612 #[test]
613 fn test_belongs_to_many_in_sql_multi_word_tables() {
614 let sql = belongs_to_many_in_sql(
615 "order_items",
616 "order_item_tag",
617 "id",
618 "tag_id",
619 "order_item_id",
620 &["1", "2"],
621 );
622 assert_eq!(
623 sql,
624 "SELECT t.* FROM order_items t INNER JOIN order_item_tag j ON t.id = j.tag_id WHERE j.order_item_id IN (1, 2)"
625 );
626 }
627
628 #[test]
629 fn test_belongs_to_many_in_sql_aligns_php_pattern() {
630 let sql = belongs_to_many_in_sql(
633 "roles",
634 "user_role",
635 "id",
636 "role_id",
637 "user_id",
638 &["1", "2"],
639 );
640 assert!(sql.starts_with("SELECT t.* FROM roles t INNER JOIN user_role j ON t.id = j.role_id WHERE j.user_id IN ("));
641 }
642
643 #[test]
648 fn test_sanitize_pk_value_numeric_integer() {
649 assert_eq!(sanitize_pk_value("1"), "1");
650 assert_eq!(sanitize_pk_value("12345"), "12345");
651 assert_eq!(sanitize_pk_value("-100"), "-100");
652 }
653
654 #[test]
655 fn test_sanitize_pk_value_numeric_float() {
656 assert_eq!(sanitize_pk_value("1.5"), "1.5");
657 assert_eq!(sanitize_pk_value("-0.5"), "-0.5");
658 }
659
660 #[test]
661 fn test_sanitize_pk_value_string() {
662 assert_eq!(sanitize_pk_value("abc"), "'abc'");
663 assert_eq!(sanitize_pk_value("uuid-123"), "'uuid-123'");
664 }
665
666 #[test]
667 fn test_sanitize_pk_value_string_with_quote() {
668 assert_eq!(sanitize_pk_value("a'b"), "'a''b'");
670 assert_eq!(sanitize_pk_value("'"), "''''");
671 }
672
673 #[test]
674 fn test_sanitize_pk_value_empty_string() {
675 assert_eq!(sanitize_pk_value(""), "''");
677 }
678
679 #[test]
684 fn test_collect_pk_values_integer_pks() {
685 let rows = vec![
686 json!({"id": 1, "name": "Alice"}),
687 json!({"id": 2, "name": "Bob"}),
688 json!({"id": 3, "name": "Charlie"}),
689 ];
690 let pks = collect_pk_values(&rows, "id");
691 assert_eq!(pks, vec!["1", "2", "3"]);
692 }
693
694 #[test]
695 fn test_collect_pk_values_string_pks() {
696 let rows = vec![
697 json!({"id": "uuid-1", "name": "Alice"}),
698 json!({"id": "uuid-2", "name": "Bob"}),
699 ];
700 let pks = collect_pk_values(&rows, "id");
701 assert_eq!(pks, vec!["uuid-1", "uuid-2"]);
702 }
703
704 #[test]
705 fn test_collect_pk_values_skip_null() {
706 let rows = vec![
708 json!({"id": 1, "name": "Alice"}),
709 json!({"id": null, "name": "Bob"}), json!({"id": 3, "name": "Charlie"}),
711 ];
712 let pks = collect_pk_values(&rows, "id");
713 assert_eq!(pks, vec!["1", "3"]);
714 }
715
716 #[test]
717 fn test_collect_pk_values_skip_missing_field() {
718 let rows = vec![
720 json!({"id": 1, "name": "Alice"}),
721 json!({"name": "Bob"}), json!({"id": 3, "name": "Charlie"}),
723 ];
724 let pks = collect_pk_values(&rows, "id");
725 assert_eq!(pks, vec!["1", "3"]);
726 }
727
728 #[test]
729 fn test_collect_pk_values_empty_rows() {
730 let rows: Vec<Value> = vec![];
731 let pks = collect_pk_values(&rows, "id");
732 assert!(pks.is_empty());
733 }
734
735 #[test]
736 fn test_collect_pk_values_custom_pk_field() {
737 let rows = vec![
738 json!({"uid": 1, "name": "Alice"}),
739 json!({"uid": 2, "name": "Bob"}),
740 ];
741 let pks = collect_pk_values(&rows, "uid");
742 assert_eq!(pks, vec!["1", "2"]);
743 }
744
745 #[test]
746 fn test_collect_pk_values_dedup_not_applied() {
747 let rows = vec![json!({"id": 1}), json!({"id": 1}), json!({"id": 2})];
749 let pks = collect_pk_values(&rows, "id");
750 assert_eq!(pks, vec!["1", "1", "2"]);
751 }
752
753 #[test]
758 fn test_group_by_fk_basic_grouping() {
759 let rows = vec![
760 json!({"id": 101, "user_id": 1, "name": "Order A"}),
761 json!({"id": 102, "user_id": 2, "name": "Order B"}),
762 json!({"id": 103, "user_id": 1, "name": "Order C"}),
763 ];
764 let grouped = group_by_fk(rows, "user_id");
765 assert_eq!(grouped.get("1").unwrap().len(), 2);
766 assert_eq!(grouped.get("2").unwrap().len(), 1);
767 }
768
769 #[test]
770 fn test_group_by_fk_string_fk() {
771 let rows = vec![
772 json!({"id": 101, "user_id": "uuid-1"}),
773 json!({"id": 102, "user_id": "uuid-2"}),
774 json!({"id": 103, "user_id": "uuid-1"}),
775 ];
776 let grouped = group_by_fk(rows, "user_id");
777 assert_eq!(grouped.get("uuid-1").unwrap().len(), 2);
778 assert_eq!(grouped.get("uuid-2").unwrap().len(), 1);
779 }
780
781 #[test]
782 fn test_group_by_fk_skip_null() {
783 let rows = vec![
785 json!({"id": 101, "user_id": 1}),
786 json!({"id": 102, "user_id": null}),
787 json!({"id": 103, "user_id": 1}),
788 ];
789 let grouped = group_by_fk(rows, "user_id");
790 assert_eq!(grouped.get("1").unwrap().len(), 2);
791 assert_eq!(grouped.get("").unwrap().len(), 1);
793 }
794
795 #[test]
796 fn test_group_by_fk_empty_rows() {
797 let rows: Vec<Value> = vec![];
798 let grouped = group_by_fk(rows, "user_id");
799 assert!(grouped.is_empty());
800 }
801
802 #[test]
803 fn test_group_by_fk_custom_fk_field() {
804 let rows = vec![json!({"id": 101, "uid": 1}), json!({"id": 102, "uid": 2})];
805 let grouped = group_by_fk(rows, "uid");
806 assert_eq!(grouped.get("1").unwrap().len(), 1);
807 assert_eq!(grouped.get("2").unwrap().len(), 1);
808 }
809
810 #[test]
811 fn test_group_by_fk_preserves_row_data() {
812 let rows = vec![json!({"id": 101, "user_id": 1, "name": "Order A", "amount": 100.5})];
814 let grouped = group_by_fk(rows, "user_id");
815 let bucket = grouped.get("1").unwrap();
816 assert_eq!(bucket.len(), 1);
817 assert_eq!(bucket[0]["id"], json!(101));
818 assert_eq!(bucket[0]["name"], json!("Order A"));
819 assert_eq!(bucket[0]["amount"], json!(100.5));
820 }
821
822 #[test]
827 fn test_parse_with_notation_simple_relation() {
828 assert_eq!(parse_with_notation("category"), ("category", None));
829 assert_eq!(parse_with_notation("dept"), ("dept", None));
830 }
831
832 #[test]
833 fn test_parse_with_notation_nested_relation() {
834 assert_eq!(
836 parse_with_notation("items.product"),
837 ("items", Some("product"))
838 );
839 assert_eq!(
840 parse_with_notation("user.profile"),
841 ("user", Some("profile"))
842 );
843 }
844
845 #[test]
846 fn test_parse_with_notation_deep_nested() {
847 assert_eq!(parse_with_notation("a.b.c"), ("a", Some("b.c")));
849 assert_eq!(
850 parse_with_notation("user.orders.items"),
851 ("user", Some("orders.items"))
852 );
853 }
854
855 #[test]
856 fn test_parse_with_notation_empty_string() {
857 assert_eq!(parse_with_notation(""), ("", None));
858 }
859
860 #[test]
861 fn test_parse_with_notation_trailing_dot() {
862 assert_eq!(parse_with_notation("relation."), ("relation", Some("")));
864 }
865
866 #[test]
867 fn test_parse_with_notation_leading_dot() {
868 assert_eq!(parse_with_notation(".sub"), ("", Some("sub")));
870 }
871
872 #[test]
877 fn test_value_to_pk_string_integer() {
878 assert_eq!(value_to_pk_string(&json!(1)), "1");
879 assert_eq!(value_to_pk_string(&json!(-100)), "-100");
880 }
881
882 #[test]
883 fn test_value_to_pk_string_float() {
884 assert_eq!(value_to_pk_string(&json!(1.5)), "1.5");
885 }
886
887 #[test]
888 fn test_value_to_pk_string_string() {
889 assert_eq!(value_to_pk_string(&json!("uuid-123")), "uuid-123");
890 }
891
892 #[test]
893 fn test_value_to_pk_string_bool() {
894 assert_eq!(value_to_pk_string(&json!(true)), "true");
895 assert_eq!(value_to_pk_string(&json!(false)), "false");
896 }
897
898 #[test]
899 fn test_value_to_pk_string_null_returns_empty() {
900 assert_eq!(value_to_pk_string(&Value::Null), "");
902 }
903
904 #[test]
905 fn test_value_to_pk_string_object_returns_empty() {
906 assert_eq!(value_to_pk_string(&json!({"a": 1})), "");
908 }
909
910 #[test]
911 fn test_value_to_pk_string_array_returns_empty() {
912 assert_eq!(value_to_pk_string(&json!([1, 2, 3])), "");
914 }
915
916 #[test]
921 fn test_r5_php_with_in_query_pattern_has_many() {
922 let sql = has_many_in_sql("orders", "user_id", &["1", "2", "3"]);
925 assert_eq!(sql, "SELECT * FROM orders WHERE user_id IN (1, 2, 3)");
926 }
927
928 #[test]
929 fn test_r5_php_with_in_query_pattern_belongs_to() {
930 let sql = belongs_to_in_sql("depts", "id", &["1", "2", "3"]);
933 assert_eq!(sql, "SELECT * FROM depts WHERE id IN (1, 2, 3)");
934 }
935
936 #[test]
937 fn test_r5_php_with_in_query_pattern_belongs_to_many() {
938 let sql = belongs_to_many_in_sql(
941 "roles",
942 "user_role",
943 "id",
944 "role_id",
945 "user_id",
946 &["1", "2"],
947 );
948 assert!(sql.contains("INNER JOIN user_role j"));
949 assert!(sql.contains("WHERE j.user_id IN (1, 2)"));
950 }
951
952 #[test]
953 fn test_r5_php_collect_range_skips_null() {
954 let rows = vec![
956 json!({"id": 1}),
957 json!({"id": null}), json!({"id": 3}),
959 ];
960 let pks = collect_pk_values(&rows, "id");
961 assert_eq!(pks, vec!["1", "3"]);
962 }
963
964 #[test]
965 fn test_r5_php_group_by_fk_matches_php_data_bucket() {
966 let rows = vec![
968 json!({"id": 101, "user_id": 1, "name": "A"}),
969 json!({"id": 102, "user_id": 2, "name": "B"}),
970 json!({"id": 103, "user_id": 1, "name": "C"}),
971 ];
972 let grouped = group_by_fk(rows, "user_id");
973 assert_eq!(grouped.get("1").unwrap().len(), 2);
975 assert_eq!(grouped.get("2").unwrap().len(), 1);
977 }
978
979 #[test]
980 fn test_r5_php_parse_with_notation_explode_dot() {
981 assert_eq!(parse_with_notation("category"), ("category", None));
983 assert_eq!(
984 parse_with_notation("items.product"),
985 ("items", Some("product"))
986 );
987 assert_eq!(parse_with_notation("a.b.c"), ("a", Some("b.c")));
989 }
990
991 #[test]
992 fn test_r5_php_has_one_in_sql_same_as_has_many() {
993 let has_one = has_one_in_sql("profiles", "user_id", &["1", "2"]);
995 let has_many = has_many_in_sql("profiles", "user_id", &["1", "2"]);
996 assert_eq!(has_one, has_many);
997 }
998
999 #[test]
1000 fn test_r5_php_empty_range_returns_in_null() {
1001 let sql_has_many = has_many_in_sql("orders", "user_id", &[]);
1004 let sql_belongs_to = belongs_to_in_sql("depts", "id", &[]);
1005 let sql_belongs_to_many =
1006 belongs_to_many_in_sql("roles", "user_role", "id", "role_id", "user_id", &[]);
1007 assert!(sql_has_many.contains("IN (NULL)"));
1008 assert!(sql_belongs_to.contains("IN (NULL)"));
1009 assert!(sql_belongs_to_many.contains("IN (NULL)"));
1010 }
1011
1012 #[test]
1013 fn test_r5_php_sanitize_pk_value_sql_escaping() {
1014 assert_eq!(sanitize_pk_value("1"), "1");
1016 assert_eq!(sanitize_pk_value("abc"), "'abc'");
1017 assert_eq!(sanitize_pk_value("a'b"), "'a''b'");
1018 }
1019
1020 #[test]
1025 fn test_integration_with_has_many_user_orders() {
1026 let users = vec![
1029 json!({"id": 1, "name": "Alice"}),
1030 json!({"id": 2, "name": "Bob"}),
1031 json!({"id": 3, "name": "Charlie"}),
1032 ];
1033 let user_pks = collect_pk_values(&users, "id");
1034 assert_eq!(user_pks, vec!["1", "2", "3"]);
1035
1036 let pk_refs: Vec<&str> = user_pks.iter().map(|s| s.as_str()).collect();
1038 let sql = has_many_in_sql("orders", "user_id", &pk_refs);
1039 assert_eq!(sql, "SELECT * FROM orders WHERE user_id IN (1, 2, 3)");
1040
1041 let orders = vec![
1043 json!({"id": 101, "user_id": 1, "amount": 100}),
1044 json!({"id": 102, "user_id": 2, "amount": 200}),
1045 json!({"id": 103, "user_id": 1, "amount": 150}),
1046 ];
1047 let grouped = group_by_fk(orders, "user_id");
1048 assert_eq!(grouped.get("1").unwrap().len(), 2);
1049 assert_eq!(grouped.get("2").unwrap().len(), 1);
1050 assert!(!grouped.contains_key("3")); }
1052
1053 #[test]
1054 fn test_integration_with_belongs_to_order_user() {
1055 let orders = vec![
1058 json!({"id": 101, "user_id": 1, "amount": 100}),
1059 json!({"id": 102, "user_id": 2, "amount": 200}),
1060 json!({"id": 103, "user_id": 1, "amount": 150}),
1061 ];
1062 let user_fks = collect_pk_values(&orders, "user_id");
1063 assert_eq!(user_fks, vec!["1", "2", "1"]);
1064
1065 let unique_fks: Vec<&str> = vec!["1", "2"];
1067 let sql = belongs_to_in_sql("users", "id", &unique_fks);
1068 assert_eq!(sql, "SELECT * FROM users WHERE id IN (1, 2)");
1069
1070 let users = vec![
1072 json!({"id": 1, "name": "Alice"}),
1073 json!({"id": 2, "name": "Bob"}),
1074 ];
1075 let grouped = group_by_fk(users, "id");
1076 assert_eq!(grouped.get("1").unwrap().len(), 1);
1077 assert_eq!(grouped.get("2").unwrap().len(), 1);
1078 }
1079
1080 #[test]
1081 fn test_integration_with_belongs_to_many_user_roles() {
1082 let users = vec![
1084 json!({"id": 1, "name": "Alice"}),
1085 json!({"id": 2, "name": "Bob"}),
1086 ];
1087 let user_pks = collect_pk_values(&users, "id");
1088 let pk_refs: Vec<&str> = user_pks.iter().map(|s| s.as_str()).collect();
1089
1090 let sql =
1092 belongs_to_many_in_sql("roles", "user_role", "id", "role_id", "user_id", &pk_refs);
1093 assert_eq!(
1094 sql,
1095 "SELECT t.* FROM roles t INNER JOIN user_role j ON t.id = j.role_id WHERE j.user_id IN (1, 2)"
1096 );
1097 }
1098
1099 #[test]
1100 fn test_integration_with_nested_notation_user_orders_items() {
1101 let (relation, sub) = parse_with_notation("orders.items");
1104 assert_eq!(relation, "orders");
1105 assert_eq!(sub, Some("items"));
1106
1107 let users = vec![json!({"id": 1, "name": "Alice"})];
1109 let user_pks = collect_pk_values(&users, "id");
1110 let pk_refs: Vec<&str> = user_pks.iter().map(|s| s.as_str()).collect();
1111 let sql = has_many_in_sql("orders", "user_id", &pk_refs);
1112 assert_eq!(sql, "SELECT * FROM orders WHERE user_id IN (1)");
1113
1114 let orders = vec![
1116 json!({"id": 101, "user_id": 1}),
1117 json!({"id": 102, "user_id": 1}),
1118 ];
1119 let order_pks = collect_pk_values(&orders, "id");
1120 let pk_refs2: Vec<&str> = order_pks.iter().map(|s| s.as_str()).collect();
1121 let sql2 = has_many_in_sql("order_items", "order_id", &pk_refs2);
1122 assert_eq!(
1123 sql2,
1124 "SELECT * FROM order_items WHERE order_id IN (101, 102)"
1125 );
1126 }
1127
1128 #[test]
1129 fn test_integration_with_has_one_user_profile() {
1130 let users = vec![
1132 json!({"id": 1, "name": "Alice"}),
1133 json!({"id": 2, "name": "Bob"}),
1134 ];
1135 let user_pks = collect_pk_values(&users, "id");
1136 let pk_refs: Vec<&str> = user_pks.iter().map(|s| s.as_str()).collect();
1137
1138 let sql = has_one_in_sql("profiles", "user_id", &pk_refs);
1140 assert_eq!(sql, "SELECT * FROM profiles WHERE user_id IN (1, 2)");
1141
1142 let profiles = vec![
1144 json!({"id": 11, "user_id": 1, "bio": "Alice's bio"}),
1145 json!({"id": 22, "user_id": 2, "bio": "Bob's bio"}),
1146 ];
1147 let grouped = group_by_fk(profiles, "user_id");
1148 let alice_profile = &grouped.get("1").unwrap()[0];
1150 assert_eq!(alice_profile["bio"], json!("Alice's bio"));
1151 }
1152
1153 #[test]
1154 fn test_integration_sanitize_pk_values_for_in_query() {
1155 let raw_pks = ["1", "abc", "2", "x'y"];
1157 let sanitized: Vec<String> = raw_pks.iter().map(|s| sanitize_pk_value(s)).collect();
1158 let joined = sanitized.join(", ");
1159 assert_eq!(joined, "1, 'abc', 2, 'x''y'");
1160
1161 let sanitized_refs: Vec<&str> = sanitized.iter().map(|s| s.as_str()).collect();
1163 let sql = has_many_in_sql("orders", "user_id", &sanitized_refs);
1164 assert_eq!(
1165 sql,
1166 "SELECT * FROM orders WHERE user_id IN (1, 'abc', 2, 'x''y')"
1167 );
1168 }
1169}