prax_query/relations/
executor.rs1use std::collections::HashMap;
14
15use crate::error::{QueryError, QueryResult};
16use crate::filter::{Filter, FilterValue};
17use crate::relations::RelationMeta;
18use crate::row::FromRow;
19use crate::traits::{Model, ModelWithPk, QueryEngine};
20
21pub async fn load_has_many<E, P, C, R>(
27 engine: &E,
28 parents: &[P],
29) -> QueryResult<HashMap<String, Vec<C>>>
30where
31 E: QueryEngine,
32 P: Model + ModelWithPk,
33 C: Model + ModelWithPk + FromRow + Send + 'static,
34 R: RelationMeta<Owner = P, Target = C>,
35{
36 let pk_values: Vec<FilterValue> = parents.iter().map(|p| p.pk_value()).collect();
37 if pk_values.is_empty() {
38 return Ok(HashMap::new());
39 }
40
41 let filter = Filter::In(R::FOREIGN_KEY.into(), pk_values);
42 let dialect = engine.dialect();
43 let (where_sql, params) = filter.to_sql(0, dialect);
44 let sql = format!("SELECT * FROM {} WHERE {}", C::TABLE_NAME, where_sql);
45
46 let children: Vec<C> = engine.query_many::<C>(&sql, params).await?;
47
48 let mut out: HashMap<String, Vec<C>> = HashMap::new();
49 for child in children {
50 let fk = child.get_column_value(R::FOREIGN_KEY).ok_or_else(|| {
51 QueryError::internal(format!(
52 "relation {}: child model missing FK column {}",
53 R::NAME,
54 R::FOREIGN_KEY
55 ))
56 })?;
57 let key = filter_value_key(&fk);
58 out.entry(key).or_default().push(child);
59 }
60 Ok(out)
61}
62
63#[doc(hidden)]
71pub fn filter_value_key_public(v: &FilterValue) -> String {
72 filter_value_key(v)
73}
74
75pub(crate) fn filter_value_key(v: &FilterValue) -> String {
84 match v {
85 FilterValue::Int(i) => i.to_string(),
86 FilterValue::String(s) => s.clone(),
87 FilterValue::Bool(b) => b.to_string(),
88 FilterValue::Float(f) => f.to_string(),
89 FilterValue::Null => "<null>".into(),
90 FilterValue::Json(v) => v.to_string(),
91 FilterValue::List(_) => {
92 panic!("relation executor does not support composite keys yet (FilterValue::List)")
93 }
94 }
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100
101 #[test]
102 fn filter_value_key_int() {
103 assert_eq!(filter_value_key(&FilterValue::Int(42)), "42");
104 }
105
106 #[test]
107 fn filter_value_key_string() {
108 assert_eq!(filter_value_key(&FilterValue::String("abc".into())), "abc");
109 }
110
111 #[test]
112 fn filter_value_key_bool() {
113 assert_eq!(filter_value_key(&FilterValue::Bool(true)), "true");
114 }
115
116 #[test]
117 fn filter_value_key_null() {
118 assert_eq!(filter_value_key(&FilterValue::Null), "<null>");
119 }
120
121 #[test]
122 #[should_panic(expected = "composite keys")]
123 fn filter_value_key_list_panics() {
124 let _ = filter_value_key(&FilterValue::List(vec![FilterValue::Int(1)]));
125 }
126}