1use crate::db::Db;
10use crate::error::OrmError;
11use crate::expr::Expr;
12use crate::model::Model;
13use crate::persist::{pk_field, value_key};
14use crate::plan::QueryPlan;
15use crate::queryset::QuerySet;
16use crate::relations::ManyToManyManager;
17use crate::signals::{M2mAction, SignalKind, SignalName};
18use crate::types::DbType;
19use crate::value::Value;
20use crate::write::{DeletePlan, InsertPlan, WritePlan};
21use std::collections::{HashMap, HashSet};
22
23impl<S: Model, T: Model> ManyToManyManager<S, T> {
24 fn owner(&self) -> Expr {
26 Expr::col(self.meta().source_column).eq(Expr::Value(self.source_pk().clone()))
27 }
28
29 pub fn queryset(&self) -> QuerySet<T> {
32 let meta = self.meta();
33 let targets = QueryPlan::from_table(meta.through_table)
34 .select(Expr::col(meta.target_column), None)
35 .filter(self.owner());
36 let pk = crate::expr::pk_column::<T>();
37 T::objects(self.db()).filter(Expr::col(pk).in_subquery(targets))
38 }
39
40 pub async fn all(&self) -> Result<Vec<T>, OrmError> {
45 self.queryset().all().await
46 }
47
48 pub async fn count(&self) -> Result<u64, OrmError> {
53 self.queryset().count().await
54 }
55
56 pub async fn contains(&self, object: &T) -> Result<bool, OrmError> {
61 self.queryset().contains(object).await
62 }
63
64 pub async fn add<'a>(&self, objects: impl IntoIterator<Item = &'a T>) -> Result<(), OrmError> {
70 self.add_pks(objects.into_iter().map(Model::pk)).await
71 }
72
73 pub async fn add_pks(&self, keys: impl IntoIterator<Item = T::Pk>) -> Result<(), OrmError> {
78 let keys = distinct(keys);
79 self.db()
80 .transaction(|tx| async move {
81 let present = self.linked(&tx, Some(&keys)).await?;
82 let missing: Vec<Value> = keys
83 .into_iter()
84 .filter(|k| !present.contains_key(&value_key(k)))
85 .collect();
86 self.link(&tx, missing).await
87 })
88 .await
89 }
90
91 pub async fn remove<'a>(
96 &self,
97 objects: impl IntoIterator<Item = &'a T>,
98 ) -> Result<u64, OrmError> {
99 self.remove_pks(objects.into_iter().map(Model::pk)).await
100 }
101
102 pub async fn remove_pks(&self, keys: impl IntoIterator<Item = T::Pk>) -> Result<u64, OrmError> {
107 let keys = distinct(keys);
108 self.db()
109 .transaction(|tx| async move { self.unlink(&tx, Some(keys)).await })
110 .await
111 }
112
113 pub async fn clear(&self) -> Result<u64, OrmError> {
118 self.db()
119 .transaction(|tx| async move { self.unlink(&tx, None).await })
120 .await
121 }
122
123 pub async fn set<'a>(&self, objects: impl IntoIterator<Item = &'a T>) -> Result<(), OrmError> {
128 self.set_pks(objects.into_iter().map(Model::pk)).await
129 }
130
131 pub async fn set_pks(&self, keys: impl IntoIterator<Item = T::Pk>) -> Result<(), OrmError> {
136 let wanted = distinct(keys);
137 self.db()
138 .transaction(|tx| async move {
139 let current = self.linked(&tx, None).await?;
140 let wanted_keys: HashSet<String> = wanted.iter().map(value_key).collect();
141 let stale: Vec<Value> = current
142 .iter()
143 .filter(|(key, _)| !wanted_keys.contains(*key))
144 .map(|(_, value)| value.clone())
145 .collect();
146 let missing: Vec<Value> = wanted
147 .into_iter()
148 .filter(|k| !current.contains_key(&value_key(k)))
149 .collect();
150 if !stale.is_empty() {
151 self.unlink(&tx, Some(stale)).await?;
152 }
153 self.link(&tx, missing).await
154 })
155 .await
156 }
157
158 async fn linked(
161 &self,
162 db: &Db,
163 among: Option<&[Value]>,
164 ) -> Result<HashMap<String, Value>, OrmError> {
165 let meta = self.meta();
166 let mut filter = self.owner();
167 if let Some(among) = among {
168 filter = filter.and(Expr::col(meta.target_column).is_in(among.iter().cloned()));
169 }
170 let plan = QueryPlan::from_table(meta.through_table)
171 .select(Expr::col(meta.target_column), None)
172 .filter(filter);
173 let mut linked = HashMap::new();
174 for row in db.fetch(&plan).await?.rows {
175 let key = row.decode_at::<T::Pk>(0)?.to_value();
177 linked.insert(value_key(&key), key);
178 }
179 Ok(linked)
180 }
181
182 async fn notify(&self, db: &Db, action: M2mAction, keys: &[Value]) -> Result<(), OrmError> {
187 let signals = db.signals();
188 if !signals.has_receivers::<S>(SignalName::M2mChanged) {
189 return Ok(());
190 }
191 let pk = pk_field::<S>()?;
192 let source = S::objects(db)
193 .get(Expr::col(pk.column).eq(Expr::Value(self.source_pk().clone())))
194 .await?;
195 signals
196 .send(
197 db,
198 &source,
199 SignalKind::M2mChanged { action },
200 Some(self.meta().name),
201 keys,
202 )
203 .await
204 }
205
206 async fn link(&self, db: &Db, keys: Vec<Value>) -> Result<(), OrmError> {
207 if keys.is_empty() {
208 return Ok(());
209 }
210 self.notify(db, M2mAction::PreAdd, &keys).await?;
211 self.insert_links(db, &keys).await?;
212 self.notify(db, M2mAction::PostAdd, &keys).await
213 }
214
215 async fn insert_links(&self, db: &Db, keys: &[Value]) -> Result<(), OrmError> {
216 let meta = self.meta();
217 let per_statement = (db.capabilities().max_params / 2).max(1);
218 for keys in keys.chunks(per_statement) {
219 let plan = WritePlan::Insert(InsertPlan {
220 table: meta.through_table.into(),
221 columns: vec![meta.source_column.into(), meta.target_column.into()],
222 rows: keys
223 .iter()
224 .map(|k| vec![self.source_pk().clone(), k.clone()])
225 .collect(),
226 returning: Vec::new(),
227 });
228 db.execute(&plan).await?;
229 }
230 Ok(())
231 }
232
233 async fn unlink(&self, db: &Db, keys: Option<Vec<Value>>) -> Result<u64, OrmError> {
235 let (pre, post, pk_set) = match &keys {
236 Some(keys) if keys.is_empty() => return Ok(0),
237 Some(keys) => (M2mAction::PreRemove, M2mAction::PostRemove, keys.as_slice()),
238 None => (M2mAction::PreClear, M2mAction::PostClear, [].as_slice()),
239 };
240 self.notify(db, pre, pk_set).await?;
241 let removed = self.delete_links(db, keys.as_deref()).await?;
242 self.notify(db, post, pk_set).await?;
243 Ok(removed)
244 }
245
246 async fn delete_links(&self, db: &Db, keys: Option<&[Value]>) -> Result<u64, OrmError> {
247 let meta = self.meta();
248 let per_statement = db.capabilities().max_params.saturating_sub(1).max(1);
249 let batches: Vec<Option<&[Value]>> = match keys {
250 Some(keys) => keys.chunks(per_statement).map(Some).collect(),
251 None => vec![None],
252 };
253 let mut removed = 0;
254 for batch in batches {
255 let mut filter = self.owner();
256 if let Some(batch) = batch {
257 filter = filter.and(Expr::col(meta.target_column).is_in(batch.iter().cloned()));
258 }
259 let plan = WritePlan::Delete(DeletePlan {
260 table: meta.through_table.into(),
261 filter: Some(filter),
262 returning: Vec::new(),
263 });
264 removed += db.execute(&plan).await?.rows_affected;
265 }
266 Ok(removed)
267 }
268}
269
270fn distinct<K: DbType>(keys: impl IntoIterator<Item = K>) -> Vec<Value> {
272 let mut seen = HashSet::new();
273 keys.into_iter()
274 .map(|k| k.to_value())
275 .filter(|v| seen.insert(value_key(v)))
276 .collect()
277}