1use crate::route::route_for;
39use crate::{Entity, Executor, Result, ShardRoute};
40use sea_query::{Alias, Expr, OnConflict, Order, Query, SimpleExpr, Value};
41
42fn col(name: &str) -> Alias {
43 Alias::new(name)
44}
45
46fn route_from_pairs<E: Entity>(pairs: &[(&'static str, Value)]) -> ShardRoute {
50 if E::shard_columns().is_empty() {
51 return ShardRoute::Global;
52 }
53 for (c, v) in pairs {
54 let r = route_for::<E>(c, v);
55 if !matches!(r, ShardRoute::Global) {
56 return r;
57 }
58 }
59 ShardRoute::Global
60}
61
62fn assign_shard_pk<E: Entity, X: Executor>(exec: &X, pairs: &[(&str, Value)]) -> Option<u64> {
69 let pk = E::primary_key();
70 if !E::shard_columns().contains(&pk) {
71 return None;
72 }
73 let pk_spec = E::columns().iter().find(|c| c.name == pk)?;
74 if pk_spec.auto_increment {
75 return None;
76 }
77 let pk_val = pairs.iter().find(|(c, _)| *c == pk).map(|(_, v)| v)?;
79 if crate::route::routing_key(pk_val) != Some(0) {
80 return None;
81 }
82 let (_, sk_val) = pairs.iter().find(|(c, v)| {
84 *c != pk && E::shard_columns().contains(c) && crate::route::routing_key(v).is_some()
85 })?;
86 let shard_key = crate::route::routing_key(sk_val)?;
87 exec.allocate_id(shard_key)
88}
89
90pub fn query<E: Entity>() -> crate::Query<E> {
94 crate::Query::new()
95}
96
97pub async fn insert<E, X>(exec: &X, entity: &E) -> Result<i64>
101where
102 E: Entity,
103 X: Executor,
104{
105 let (route, sql, params, assigned) = {
109 let mut pairs = entity.insert_values();
110 let assigned = assign_shard_pk::<E, X>(exec, &pairs);
112 if let Some(id) = assigned {
113 let pk = E::primary_key();
114 for (c, v) in pairs.iter_mut() {
115 if *c == pk {
116 *v = id.into();
117 }
118 }
119 }
120 let route = route_from_pairs::<E>(&pairs);
121 let mut stmt = Query::insert();
122 stmt.into_table(col(E::table()));
123 stmt.columns(pairs.iter().map(|(c, _)| col(c)));
124 stmt.values_panic(pairs.into_iter().map(|(_, v)| SimpleExpr::from(Expr::val(v))));
125
126 let (sql, params) = exec.dialect().build_query(&stmt);
127 (route, sql, into_vec(params), assigned)
128 };
129
130 let outcome = exec.execute_routed(route, &sql, params).await?;
131 Ok(assigned.map(|i| i as i64).unwrap_or(outcome.last_insert_id))
134}
135
136pub async fn upsert<E, X>(
142 exec: &X,
143 entity: &E,
144 conflict_columns: &[&str],
145 update_columns: &[&str],
146) -> Result<u64>
147where
148 E: Entity,
149 X: Executor,
150{
151 let (route, sql, params) = {
152 let pairs = entity.insert_values();
153 let route = route_from_pairs::<E>(&pairs);
154 let mut stmt = Query::insert();
155 stmt.into_table(col(E::table()));
156 stmt.columns(pairs.iter().map(|(c, _)| col(c)));
157 stmt.values_panic(pairs.into_iter().map(|(_, v)| SimpleExpr::from(Expr::val(v))));
158
159 let mut on_conflict = OnConflict::columns(conflict_columns.iter().map(|c| col(c)));
160 if !update_columns.is_empty() {
161 on_conflict.update_columns(update_columns.iter().map(|c| col(c)));
162 } else if let Some(c) = conflict_columns.first() {
163 on_conflict.value(col(c), Expr::col(col(c)));
168 } else {
169 on_conflict.do_nothing();
170 }
171 stmt.on_conflict(on_conflict);
172
173 let (sql, params) = exec.dialect().build_query(&stmt);
174 (route, sql, into_vec(params))
175 };
176
177 Ok(exec.execute_routed(route, &sql, params).await?.rows_affected)
178}
179
180pub async fn find_by_pk<E, X, V>(exec: &X, pk: V) -> Result<Option<E>>
183where
184 E: Entity,
185 X: Executor,
186 V: Into<Value>,
187{
188 let (route, sql, params) = {
189 let pk_val: Value = pk.into();
190 let route = route_for::<E>(E::primary_key(), &pk_val);
191 let mut stmt = Query::select();
192 stmt.from(col(E::table()))
193 .columns(E::columns().iter().map(|c| col(c.name)))
194 .and_where(Expr::col(col(E::primary_key())).eq(pk_val))
195 .limit(1);
196
197 let (sql, params) = exec.dialect().build_query(&stmt);
198 (route, sql, into_vec(params))
199 };
200
201 let rows = exec.fetch_all_routed(route, &sql, params).await?;
202 match rows.first() {
203 Some(row) => Ok(Some(E::from_row(row.as_ref())?)),
204 None => Ok(None),
205 }
206}
207
208pub async fn find_by<E, X, V>(exec: &X, column: &str, value: V) -> Result<Vec<E>>
216where
217 E: Entity,
218 X: Executor,
219 V: Into<Value>,
220{
221 let (route, sql, params) = {
222 let val: Value = value.into();
223 let route = route_for::<E>(column, &val);
224 let mut stmt = Query::select();
225 stmt.from(col(E::table()))
226 .columns(E::columns().iter().map(|c| col(c.name)))
227 .and_where(Expr::col(col(column)).eq(val));
228
229 let (sql, params) = exec.dialect().build_query(&stmt);
230 (route, sql, into_vec(params))
231 };
232
233 let rows = exec.fetch_all_routed(route, &sql, params).await?;
234 rows.iter().map(|r| E::from_row(r.as_ref())).collect()
235}
236
237pub async fn find_one_by<E, X, V>(exec: &X, column: &str, value: V) -> Result<Option<E>>
240where
241 E: Entity,
242 X: Executor,
243 V: Into<Value>,
244{
245 let (route, sql, params) = {
246 let val: Value = value.into();
247 let route = route_for::<E>(column, &val);
248 let mut stmt = Query::select();
249 stmt.from(col(E::table()))
250 .columns(E::columns().iter().map(|c| col(c.name)))
251 .and_where(Expr::col(col(column)).eq(val))
252 .limit(1);
253
254 let (sql, params) = exec.dialect().build_query(&stmt);
255 (route, sql, into_vec(params))
256 };
257
258 let rows = exec.fetch_all_routed(route, &sql, params).await?;
259 match rows.first() {
260 Some(row) => Ok(Some(E::from_row(row.as_ref())?)),
261 None => Ok(None),
262 }
263}
264
265pub async fn all<E, X>(exec: &X) -> Result<Vec<E>>
270where
271 E: Entity,
272 X: Executor,
273{
274 let (sql, params) = {
275 let mut stmt = Query::select();
276 stmt.from(col(E::table())).columns(E::columns().iter().map(|c| col(c.name)));
277
278 let (sql, params) = exec.dialect().build_query(&stmt);
279 (sql, into_vec(params))
280 };
281
282 let rows = exec.fetch_all_routed(ShardRoute::Global, &sql, params).await?;
283 rows.iter().map(|r| E::from_row(r.as_ref())).collect()
284}
285
286pub async fn update<E, X>(exec: &X, entity: &E) -> Result<u64>
289where
290 E: Entity,
291 X: Executor,
292{
293 let (route, sql, params) = {
294 let pk_val = entity.pk_value();
295 let route = route_for::<E>(E::primary_key(), &pk_val);
296 let mut stmt = Query::update();
297 stmt.table(col(E::table()));
298 for (c, v) in entity.update_values() {
299 stmt.value(col(c), v);
300 }
301 stmt.and_where(Expr::col(col(E::primary_key())).eq(pk_val));
302
303 let (sql, params) = exec.dialect().build_query(&stmt);
304 (route, sql, into_vec(params))
305 };
306
307 let outcome = exec.execute_routed(route, &sql, params).await?;
308 Ok(outcome.rows_affected)
309}
310
311pub async fn delete_by_pk<E, X, V>(exec: &X, pk: V) -> Result<u64>
313where
314 E: Entity,
315 X: Executor,
316 V: Into<Value>,
317{
318 let (route, sql, params) = {
319 let pk_val: Value = pk.into();
320 let route = route_for::<E>(E::primary_key(), &pk_val);
321 let mut stmt = Query::delete();
322 stmt.from_table(col(E::table())).and_where(Expr::col(col(E::primary_key())).eq(pk_val));
323
324 let (sql, params) = exec.dialect().build_query(&stmt);
325 (route, sql, into_vec(params))
326 };
327
328 let outcome = exec.execute_routed(route, &sql, params).await?;
329 Ok(outcome.rows_affected)
330}
331
332pub fn cursor<E, X>(exec: &X, page_size: u64) -> Cursor<'_, E, X>
335where
336 E: Entity,
337 X: Executor,
338{
339 Cursor::new(exec, page_size)
340}
341
342pub struct Cursor<'a, E, X> {
359 exec: &'a X,
360 page_size: u64,
361 after: Option<Value>,
362 done: bool,
363 _entity: core::marker::PhantomData<E>,
364}
365
366impl<'a, E, X> Cursor<'a, E, X>
367where
368 E: Entity,
369 X: Executor,
370{
371 fn new(exec: &'a X, page_size: u64) -> Self {
372 Self {
373 exec,
374 page_size: page_size.max(1),
375 after: None,
376 done: false,
377 _entity: core::marker::PhantomData,
378 }
379 }
380
381 pub fn after(mut self, pk: impl Into<Value>) -> Self {
385 self.after = Some(pk.into());
386 self
387 }
388
389 pub async fn next_page(&mut self) -> Result<Option<Vec<E>>> {
393 if self.done {
394 return Ok(None);
395 }
396
397 let pk = E::primary_key();
398 let (route, sql, params) = {
399 let mut stmt = Query::select();
400 stmt.from(col(E::table()))
401 .columns(E::columns().iter().map(|c| col(c.name)))
402 .order_by(col(pk), Order::Asc)
403 .limit(self.page_size);
404 if let Some(after) = &self.after {
405 stmt.and_where(Expr::col(col(pk)).gt(after.clone()));
406 }
407
408 let route = match &self.after {
411 Some(after) => route_for::<E>(pk, after),
412 None => ShardRoute::Global,
413 };
414 let (sql, params) = self.exec.dialect().build_query(&stmt);
415 (route, sql, into_vec(params))
416 };
417
418 let rows = self.exec.fetch_all_routed(route, &sql, params).await?;
419 if rows.is_empty() {
420 self.done = true;
421 return Ok(None);
422 }
423
424 let page: Vec<E> = rows.iter().map(|r| E::from_row(r.as_ref())).collect::<Result<_>>()?;
425
426 if let Some(last) = rows.last() {
428 self.after = Some(pk_from_row::<E>(last.as_ref())?);
429 }
430 if (rows.len() as u64) < self.page_size {
431 self.done = true;
432 }
433 Ok(Some(page))
434 }
435
436 pub fn checkpoint(&self) -> Option<&Value> {
441 self.after.as_ref()
442 }
443}
444
445fn pk_from_row<E: Entity>(row: &dyn crate::Row) -> Result<Value> {
448 use crate::ColumnType;
449 let pk = E::primary_key();
450 let spec = E::columns()
451 .iter()
452 .find(|c| c.name == pk)
453 .ok_or_else(|| crate::Error::msg("primary key column not in metadata"))?;
454 let null = || crate::Error::msg(format!("primary key `{pk}` was NULL"));
455 Ok(match spec.ty {
456 ColumnType::Bool => row.get_bool(pk)?.ok_or_else(null)?.into(),
457 ColumnType::Int => row.get_i32(pk)?.ok_or_else(null)?.into(),
458 ColumnType::BigInt => row.get_i64(pk)?.ok_or_else(null)?.into(),
459 ColumnType::Uint => row.get_u32(pk)?.ok_or_else(null)?.into(),
460 ColumnType::BigUint => row.get_u64(pk)?.ok_or_else(null)?.into(),
461 ColumnType::Double => row.get_f64(pk)?.ok_or_else(null)?.into(),
462 ColumnType::Text => row.get_string(pk)?.ok_or_else(null)?.into(),
463 ColumnType::Binary => row.get_bytes(pk)?.ok_or_else(null)?.into(),
464 ColumnType::Timestamp => row.get_datetime(pk)?.ok_or_else(null)?.into(),
465 ColumnType::Date => row.get_naive_date(pk)?.ok_or_else(null)?.into(),
466 })
467}
468
469fn into_vec(values: sea_query::Values) -> Vec<Value> {
472 values.0
473}