1use std::sync::Arc;
44
45use rainier_orm::sea_query::Value;
46use rainier_orm::{Dialect, ExecOutcome, Executor, Row, ShardRoute};
47use rainier_support::{BoxFuture, Error, Result};
48
49use crate::row::{ColumnRequest, OwnedRow};
50use crate::statement::Prepared;
51
52pub trait Connection: Send + Sync + 'static {
57 fn dialect(&self) -> Dialect;
59
60 fn shard_family(&self) -> Option<String> {
63 None
64 }
65
66 fn allocate_id(&self, shard_key: u64) -> Option<u64> {
68 let _ = shard_key;
69 None
70 }
71
72 fn fetch<'a>(
76 &'a self,
77 route: ShardRoute,
78 sql: &'a str,
79 params: Vec<Value>,
80 columns: Vec<ColumnRequest>,
81 ) -> BoxFuture<'a, Result<Vec<OwnedRow>>>;
82
83 fn execute<'a>(
85 &'a self,
86 route: ShardRoute,
87 sql: &'a str,
88 params: Vec<Value>,
89 ) -> BoxFuture<'a, Result<Vec<ExecOutcome>>>;
90
91 fn fetch_raw<'a>(
102 &'a self,
103 route: ShardRoute,
104 sql: &'a str,
105 params: Vec<Value>,
106 ) -> BoxFuture<'a, Result<Vec<Box<dyn Row>>>>;
107}
108
109#[macro_export]
130macro_rules! bind_executor {
131 ($executor:ty) => {
132 impl $crate::Connection for $executor {
133 fn dialect(&self) -> $crate::reexport::Dialect {
134 $crate::reexport::Executor::dialect(self)
135 }
136
137 fn shard_family(&self) -> ::std::option::Option<::std::string::String> {
138 $crate::reexport::Executor::shard_family(self)
139 .map(::std::string::ToString::to_string)
140 }
141
142 fn allocate_id(&self, shard_key: u64) -> ::std::option::Option<u64> {
143 $crate::reexport::Executor::allocate_id(self, shard_key)
144 }
145
146 fn fetch<'a>(
147 &'a self,
148 route: $crate::reexport::ShardRoute,
149 sql: &'a str,
150 params: ::std::vec::Vec<$crate::reexport::Value>,
151 columns: ::std::vec::Vec<$crate::reexport::ColumnRequest>,
152 ) -> $crate::reexport::BoxFuture<
153 'a,
154 $crate::reexport::Result<::std::vec::Vec<$crate::reexport::OwnedRow>>,
155 > {
156 ::std::boxed::Box::pin(async move {
157 let rows =
161 $crate::reexport::Executor::fetch_all_routed(self, route, sql, params)
162 .await
163 .map_err($crate::reexport::Error::from)?;
164
165 let mut owned = ::std::vec::Vec::with_capacity(rows.len());
166 for row in &rows {
167 owned.push(
168 $crate::reexport::OwnedRow::snapshot(row.as_ref(), &columns)
169 .map_err($crate::reexport::Error::from)?,
170 );
171 }
172 ::std::result::Result::Ok(owned)
173 })
174 }
175
176 fn execute<'a>(
177 &'a self,
178 route: $crate::reexport::ShardRoute,
179 sql: &'a str,
180 params: ::std::vec::Vec<$crate::reexport::Value>,
181 ) -> $crate::reexport::BoxFuture<
182 'a,
183 $crate::reexport::Result<::std::vec::Vec<$crate::reexport::ExecOutcome>>,
184 > {
185 ::std::boxed::Box::pin(async move {
186 let outcome =
187 $crate::reexport::Executor::execute_routed(self, route, sql, params)
188 .await
189 .map_err($crate::reexport::Error::from)?;
190 ::std::result::Result::Ok(::std::vec![outcome])
191 })
192 }
193
194 fn fetch_raw<'a>(
195 &'a self,
196 route: $crate::reexport::ShardRoute,
197 sql: &'a str,
198 params: ::std::vec::Vec<$crate::reexport::Value>,
199 ) -> $crate::reexport::BoxFuture<
200 'a,
201 $crate::reexport::Result<
202 ::std::vec::Vec<::std::boxed::Box<dyn $crate::reexport::Row>>,
203 >,
204 > {
205 ::std::boxed::Box::pin(async move {
206 $crate::reexport::Executor::fetch_all_routed(self, route, sql, params)
207 .await
208 .map_err($crate::reexport::Error::from)
209 })
210 }
211 }
212 };
213}
214
215#[cfg(feature = "sea-orm-executor")]
219crate::bind_executor!(rainier_drivers::sql::SeaOrmExecutor);
220
221#[derive(Clone)]
226pub struct Database {
227 connection: Arc<dyn Connection>,
228}
229
230impl Database {
231 pub fn new(connection: impl Connection) -> Self {
233 Self { connection: Arc::new(connection) }
234 }
235
236 pub fn from_arc(connection: Arc<dyn Connection>) -> Self {
238 Self { connection }
239 }
240
241 pub fn connection(&self) -> &Arc<dyn Connection> {
243 &self.connection
244 }
245
246 pub fn dialect(&self) -> Dialect {
248 self.connection.dialect()
249 }
250
251 pub fn is_sharded(&self) -> bool {
253 self.connection.shard_family().is_some()
254 }
255
256 pub fn shard_family(&self) -> Option<String> {
258 self.connection.shard_family()
259 }
260
261 pub async fn fetch(
265 &self,
266 prepared: Prepared,
267 columns: Vec<ColumnRequest>,
268 ) -> Result<Vec<OwnedRow>> {
269 self.connection.fetch(prepared.route, &prepared.sql, prepared.params, columns).await
270 }
271
272 pub async fn fetch_all<E: rainier_orm::Entity>(&self, prepared: Prepared) -> Result<Vec<E>> {
274 let rows = self.fetch(prepared, ColumnRequest::for_entity::<E>()).await?;
275 rows.iter().map(|row| E::from_row(row).map_err(Error::from)).collect()
276 }
277
278 pub async fn fetch_one<E: rainier_orm::Entity>(&self, prepared: Prepared) -> Result<Option<E>> {
280 let rows = self.fetch(prepared, ColumnRequest::for_entity::<E>()).await?;
281 match rows.first() {
282 Some(row) => Ok(Some(E::from_row(row).map_err(Error::from)?)),
283 None => Ok(None),
284 }
285 }
286
287 pub async fn fetch_count(&self, prepared: Prepared) -> Result<u64> {
289 let columns = vec![ColumnRequest::new("cnt", rainier_orm::ColumnType::BigInt)];
290 let rows = self.fetch(prepared, columns).await?;
291
292 let count = match rows.first() {
293 Some(row) => row.get_i64("cnt").map_err(Error::from)?.unwrap_or(0),
294 None => 0,
295 };
296 Ok(count.max(0) as u64)
298 }
299
300 pub async fn execute(&self, prepared: Prepared) -> Result<ExecOutcome> {
302 let outcomes =
303 self.connection.execute(prepared.route, &prepared.sql, prepared.params).await?;
304 Ok(outcomes.into_iter().next().unwrap_or_default())
305 }
306
307 pub async fn statement(&self, sql: &str) -> Result<ExecOutcome> {
309 let outcomes = self.connection.execute(ShardRoute::Global, sql, Vec::new()).await?;
310 Ok(outcomes.into_iter().next().unwrap_or_default())
311 }
312}
313
314impl std::fmt::Debug for Database {
315 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
316 f.debug_struct("Database")
317 .field("dialect", &self.connection.dialect())
318 .field("shard_family", &self.connection.shard_family())
319 .finish()
320 }
321}
322
323impl Executor for Database {
331 fn dialect(&self) -> Dialect {
332 self.connection.dialect()
333 }
334
335 fn shard_family(&self) -> Option<&str> {
336 None
341 }
342
343 fn is_sharded(&self) -> bool {
344 self.connection.shard_family().is_some()
345 }
346
347 fn allocate_id(&self, shard_key: u64) -> Option<u64> {
348 self.connection.allocate_id(shard_key)
349 }
350
351 async fn fetch_all(
352 &self,
353 sql: &str,
354 params: Vec<Value>,
355 ) -> rainier_orm::Result<Vec<Box<dyn Row>>> {
356 self.connection.fetch_raw(ShardRoute::Global, sql, params).await.map_err(into_anyhow)
357 }
358
359 async fn execute(&self, sql: &str, params: Vec<Value>) -> rainier_orm::Result<ExecOutcome> {
360 let outcomes =
361 self.connection.execute(ShardRoute::Global, sql, params).await.map_err(into_anyhow)?;
362 Ok(outcomes.into_iter().next().unwrap_or_default())
363 }
364
365 async fn fetch_all_routed(
366 &self,
367 route: ShardRoute,
368 sql: &str,
369 params: Vec<Value>,
370 ) -> rainier_orm::Result<Vec<Box<dyn Row>>> {
371 self.connection.fetch_raw(route, sql, params).await.map_err(into_anyhow)
372 }
373
374 async fn execute_routed(
375 &self,
376 route: ShardRoute,
377 sql: &str,
378 params: Vec<Value>,
379 ) -> rainier_orm::Result<ExecOutcome> {
380 let outcomes = self.connection.execute(route, sql, params).await.map_err(into_anyhow)?;
381 Ok(outcomes.into_iter().next().unwrap_or_default())
382 }
383}
384
385fn into_anyhow(error: Error) -> rainier_orm::Error {
387 anyhow::anyhow!("{error}")
388}
389
390#[cfg(test)]
391mod tests {
392 use super::*;
393 use crate::statement;
394 use crate::testing::{fake_database, MemoryConnection};
395
396 #[derive(rainier_orm::Entity, Clone, Debug, PartialEq)]
397 #[orm(table = "posts")]
398 struct Post {
399 #[orm(pk, auto_increment)]
400 id: u64,
401 title: String,
402 }
403
404 fn row(id: u64, title: &str) -> OwnedRow {
405 OwnedRow::new().with("id", id).with("title", title)
406 }
407
408 #[test]
409 fn the_send_query_path_is_actually_send() {
410 fn assert_send<T: Send>(_: T) {}
414
415 let (db, _) = fake_database(MemoryConnection::new(Dialect::Sqlite));
416 assert_send(async move {
417 let prepared = statement::select_all::<Post>(Dialect::Sqlite);
418 let _: Vec<Post> = db.fetch_all(prepared).await.unwrap();
419 });
420 }
421
422 #[test]
423 fn a_database_reports_its_backend() {
424 let db = Database::new(MemoryConnection::new(Dialect::Postgres));
425 assert_eq!(db.dialect(), Dialect::Postgres);
426 assert!(!db.is_sharded());
427 assert_eq!(db.shard_family(), None);
428 }
429
430 #[tokio::test]
431 async fn fetch_all_decodes_entities() {
432 let (db, _) = fake_database(
433 MemoryConnection::new(Dialect::Sqlite).returning([row(1, "a"), row(2, "b")]),
434 );
435
436 let posts: Vec<Post> =
437 db.fetch_all(statement::select_all::<Post>(Dialect::Sqlite)).await.unwrap();
438
439 assert_eq!(posts.len(), 2);
440 assert_eq!(posts[1], Post { id: 2, title: "b".into() });
441 }
442
443 #[tokio::test]
444 async fn fetch_one_returns_none_for_no_rows() {
445 let (db, _) = fake_database(MemoryConnection::new(Dialect::Sqlite));
446 let found: Option<Post> = db
447 .fetch_one(statement::select_by_pk::<Post>(Dialect::Sqlite, 1_i64.into()))
448 .await
449 .unwrap();
450 assert!(found.is_none());
451 }
452
453 #[tokio::test]
454 async fn fetch_count_reads_the_cnt_column() {
455 let (db, _) = fake_database(
456 MemoryConnection::new(Dialect::Sqlite).returning([OwnedRow::new().with("cnt", 42_i64)]),
457 );
458
459 let criteria = crate::Criteria::new();
460 let prepared = statement::count_matching::<Post>(Dialect::Sqlite, &criteria);
461 assert_eq!(db.fetch_count(prepared).await.unwrap(), 42);
462 }
463
464 #[tokio::test]
465 async fn fetch_count_is_zero_when_nothing_comes_back() {
466 let (db, _) = fake_database(MemoryConnection::new(Dialect::Sqlite));
467 let criteria = crate::Criteria::new();
468 let prepared = statement::count_matching::<Post>(Dialect::Sqlite, &criteria);
469 assert_eq!(db.fetch_count(prepared).await.unwrap(), 0);
470 }
471
472 #[tokio::test]
473 async fn statements_and_bindings_reach_the_connection() {
474 let (db, connection) = fake_database(MemoryConnection::new(Dialect::Sqlite));
475
476 db.execute(statement::delete_by_pk::<Post>(Dialect::Sqlite, 3_i64.into())).await.unwrap();
477
478 let sql = connection.last_statement().unwrap();
479 assert!(sql.starts_with("DELETE FROM"), "{sql}");
480 assert_eq!(connection.bindings()[0].len(), 1);
481 }
482
483 #[tokio::test]
484 async fn ddl_runs_through_statement() {
485 let (db, connection) = fake_database(MemoryConnection::new(Dialect::Sqlite));
486 db.statement("CREATE TABLE posts (id INTEGER)").await.unwrap();
487 assert_eq!(connection.last_statement().as_deref(), Some("CREATE TABLE posts (id INTEGER)"));
488 }
489
490 #[tokio::test]
491 async fn an_error_survives_the_round_trip() {
492 let (db, _) =
493 fake_database(MemoryConnection::new(Dialect::Sqlite).failing("table is locked"));
494
495 let err = db.statement("SELECT 1").await.unwrap_err();
496 assert!(err.message().contains("table is locked"), "{}", err.message());
497 }
498
499 #[tokio::test]
500 async fn the_orm_surface_is_still_reachable_through_the_executor_impl() {
501 let (db, connection) = fake_database(MemoryConnection::new(Dialect::Sqlite));
504
505 let outcome = Executor::execute(&db, "DELETE FROM posts", vec![]).await.unwrap();
506 assert_eq!(outcome.rows_affected, 1);
507 assert_eq!(connection.last_statement().as_deref(), Some("DELETE FROM posts"));
508 }
509
510 #[test]
511 fn cloning_shares_one_connection() {
512 let db = Database::new(MemoryConnection::new(Dialect::MySql));
513 assert!(Arc::ptr_eq(db.connection(), db.clone().connection()));
514 }
515}