1use crate::backend::{Backend, ExecResult, QueryResult};
20use crate::capabilities::BackendCapabilities;
21use crate::error::{OrmError, QueryError};
22use crate::model::{Model, ModelMeta};
23use crate::plan::{PlanOrigin, QueryPlan};
24use crate::queryset::QuerySet;
25use crate::router::DatabaseRouter;
26use crate::signals::Signals;
27use crate::value::Value;
28use crate::write::WritePlan;
29use std::collections::BTreeMap;
30use std::future::Future;
31use std::sync::Arc;
32use std::time::Instant;
33use tracing::Instrument;
34
35mod transaction;
36use transaction::{ConnState, ExecutorRef, TxState};
37
38#[derive(Clone)]
39enum Target {
40 Pool(Arc<dyn Backend>),
41 Tx(Arc<TxState>),
42 Conn(Arc<ConnState>),
43}
44
45#[derive(Clone)]
47pub struct Db {
48 target: Target,
49 signals: Signals,
50}
51
52impl std::fmt::Debug for Db {
53 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54 let kind = self.capabilities().kind;
55 f.debug_struct("Db")
56 .field("backend", &kind)
57 .field("in_transaction", &self.in_transaction())
58 .finish()
59 }
60}
61
62impl Db {
63 pub fn new(backend: impl Backend) -> Self {
65 Self::from_arc(Arc::new(backend))
66 }
67
68 pub fn from_arc(backend: Arc<dyn Backend>) -> Self {
70 Self {
71 target: Target::Pool(backend),
72 signals: Signals::default(),
73 }
74 }
75
76 #[must_use]
81 pub fn with_signals(mut self, signals: Signals) -> Self {
82 self.signals = signals;
83 self
84 }
85
86 pub fn signals(&self) -> &Signals {
88 &self.signals
89 }
90
91 pub fn same_database(&self, other: &Db) -> bool {
94 std::ptr::addr_eq(Arc::as_ptr(self.pool()), Arc::as_ptr(other.pool()))
95 }
96
97 pub(crate) fn origin(&self) -> PlanOrigin {
99 PlanOrigin::new(Arc::as_ptr(self.pool()).cast::<()>() as usize)
100 }
101
102 fn ensure_local(&self, foreign: bool) -> Result<(), OrmError> {
104 if foreign {
105 return Err(QueryError::InvalidPlan(
106 "a subquery built against another database cannot run here".into(),
107 )
108 .into());
109 }
110 Ok(())
111 }
112
113 fn pool(&self) -> &Arc<dyn Backend> {
114 match &self.target {
115 Target::Pool(pool) => pool,
116 Target::Tx(state) => &state.pool,
117 Target::Conn(state) => &state.pool,
118 }
119 }
120
121 fn executor(&self) -> Result<ExecutorRef<'_>, OrmError> {
122 match &self.target {
123 Target::Pool(pool) => Ok(ExecutorRef::Pool(pool.as_ref())),
124 Target::Tx(state) => {
125 let lease = state.control.lease(state.scope)?;
126 Ok(ExecutorRef::Leased(lease))
127 }
128 Target::Conn(state) => Ok(ExecutorRef::Leased(state.control.lease(0)?)),
129 }
130 }
131
132 pub fn capabilities(&self) -> BackendCapabilities {
134 self.pool().capabilities()
135 }
136
137 pub fn read_parameter_count(&self, plan: &QueryPlan) -> Result<Option<usize>, OrmError> {
148 self.ensure_local(plan.has_foreign_origin(self.origin()))?;
149 self.pool().read_parameter_count(plan)
150 }
151
152 pub fn in_transaction(&self) -> bool {
154 matches!(self.target, Target::Tx(_))
155 }
156
157 pub fn is_scoped(&self) -> bool {
162 !matches!(self.target, Target::Pool(_))
163 }
164
165 pub async fn fetch(&self, plan: &QueryPlan) -> Result<QueryResult, OrmError> {
170 self.ensure_local(plan.has_foreign_origin(self.origin()))?;
171 let mut executor = self.executor()?;
172 let query = executor.as_ref().fetch(plan);
173 let result = self.traced("select", &plan.source.name, query).await;
174 executor.finish();
175 result
176 }
177
178 pub async fn execute(&self, plan: &WritePlan) -> Result<ExecResult, OrmError> {
183 let origin = self.origin();
184 let foreign = match plan {
185 WritePlan::Insert(_) => false,
186 WritePlan::Update(p) => p
187 .assignments
188 .iter()
189 .map(|(_, e)| e)
190 .chain(p.filter.iter())
191 .any(|e| e.has_foreign_origin(origin)),
192 WritePlan::Delete(p) => p.filter.iter().any(|e| e.has_foreign_origin(origin)),
193 };
194 self.ensure_local(foreign)?;
195 let (operation, table) = match plan {
196 WritePlan::Insert(p) => ("insert", &p.table),
197 WritePlan::Update(p) => ("update", &p.table),
198 WritePlan::Delete(p) => ("delete", &p.table),
199 };
200 let mut executor = self.executor()?;
201 let result = self
202 .traced(operation, table, executor.as_ref().execute(plan))
203 .await;
204 executor.finish();
205 result
206 }
207
208 pub async fn raw_sql(&self, sql: &str, params: Vec<Value>) -> Result<QueryResult, OrmError> {
216 tracing::trace!(sql, "raw query");
217 let mut executor = self.executor()?;
218 let result = self
219 .traced("raw", "", executor.as_ref().fetch_raw(sql, params))
220 .await;
221 executor.finish();
222 result
223 }
224
225 pub async fn raw_execute(&self, sql: &str, params: Vec<Value>) -> Result<u64, OrmError> {
230 tracing::trace!(sql, "raw statement");
231 let mut executor = self.executor()?;
232 let result = self
233 .traced("raw", "", executor.as_ref().execute_raw(sql, params))
234 .await;
235 executor.finish();
236 result
237 }
238
239 pub async fn execute_script(&self, sql: &str) -> Result<(), OrmError> {
244 tracing::trace!(sql, "script");
245 let mut executor = self.executor()?;
246 let result = self
247 .traced("script", "", executor.as_ref().execute_script(sql))
248 .await;
249 executor.finish();
250 result
251 }
252
253 async fn traced<T>(
260 &self,
261 operation: &'static str,
262 table: &str,
263 query: impl Future<Output = Result<T, OrmError>>,
264 ) -> Result<T, OrmError> {
265 let span = tracing::debug_span!(
266 "orm.query",
267 db.system = self.capabilities().kind.system_name(),
268 db.operation = operation,
269 db.table = table,
270 elapsed_ms = tracing::field::Empty,
271 );
272 let recorder = span.clone();
273 async move {
274 let started = Instant::now();
275 let result = query.await;
276 recorder.record("elapsed_ms", started.elapsed().as_secs_f64() * 1000.0);
277 result
278 }
279 .instrument(span)
280 .await
281 }
282}
283
284#[derive(Clone, Default)]
293pub struct Databases {
294 by_alias: BTreeMap<String, Db>,
295 router: Option<Arc<dyn DatabaseRouter>>,
296}
297
298impl std::fmt::Debug for Databases {
299 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
300 f.debug_struct("Databases")
301 .field("aliases", &self.by_alias.keys().collect::<Vec<_>>())
302 .field("router", &self.router.is_some())
303 .finish()
304 }
305}
306
307impl Databases {
308 pub const DEFAULT: &'static str = "default";
310
311 pub fn new() -> Self {
313 Self::default()
314 }
315
316 #[must_use]
318 pub fn with(mut self, alias: impl Into<String>, db: Db) -> Self {
319 self.by_alias.insert(alias.into(), db);
320 self
321 }
322
323 #[must_use]
325 pub fn with_router(mut self, router: impl DatabaseRouter) -> Self {
326 self.router = Some(Arc::new(router));
327 self
328 }
329
330 pub fn get(&self, alias: &str) -> Option<&Db> {
332 self.by_alias.get(alias)
333 }
334
335 pub fn default_db(&self) -> Option<&Db> {
337 self.get(Self::DEFAULT)
338 }
339
340 pub fn aliases(&self) -> impl Iterator<Item = &str> {
342 self.by_alias.keys().map(String::as_str)
343 }
344
345 pub fn for_read<M: Model>(&self) -> Result<&Db, OrmError> {
350 self.resolve(self.router.as_deref().and_then(|r| r.db_for_read(M::META)))
351 }
352
353 pub fn for_write<M: Model>(&self) -> Result<&Db, OrmError> {
358 self.resolve(self.router.as_deref().and_then(|r| r.db_for_write(M::META)))
359 }
360
361 pub fn objects<M: Model>(&self) -> Result<QuerySet<M>, OrmError> {
367 self.for_read::<M>().map(M::objects)
368 }
369
370 pub fn using<M: Model>(&self, alias: &str) -> Result<QuerySet<M>, OrmError> {
376 self.resolve(Some(alias)).map(M::objects)
377 }
378
379 pub fn allow_migrate(&self, alias: &str, model: &ModelMeta) -> bool {
382 self.router
383 .as_deref()
384 .is_none_or(|r| r.allow_migrate(alias, model))
385 }
386
387 fn resolve(&self, alias: Option<&str>) -> Result<&Db, OrmError> {
388 let alias = alias.unwrap_or(Self::DEFAULT);
389 self.get(alias)
390 .ok_or_else(|| OrmError::UnknownDatabase(alias.to_owned()))
391 }
392}
393
394#[macro_export]
396macro_rules! params {
397 ($($value:expr),* $(,)?) => {
398 ::std::vec![$($crate::Value::from($value)),*]
399 };
400}
401
402#[cfg(test)]
403mod tests {
404 use super::*;
405 use crate::backend::{ExecResult, Executor, QueryResult, Transaction};
406 use crate::capabilities::BackendCapabilities;
407 use crate::plan::QueryPlan;
408 use crate::write::DeletePlan;
409 use async_trait::async_trait;
410 use std::sync::Mutex;
411 use tracing::field::{Field, Visit};
412 use tracing::span::{Attributes, Id, Record};
413 use tracing::{Event, Subscriber};
414 use tracing_subscriber::Layer;
415 use tracing_subscriber::layer::{Context, SubscriberExt};
416 use tracing_subscriber::registry::LookupSpan;
417
418 struct Stub {
420 _id: u8,
421 }
422
423 #[async_trait]
424 impl Executor for Stub {
425 fn capabilities(&self) -> BackendCapabilities {
426 BackendCapabilities::sqlite()
427 }
428 async fn fetch(&self, _: &QueryPlan) -> Result<QueryResult, OrmError> {
429 Ok(QueryResult::default())
430 }
431 async fn execute(&self, _: &WritePlan) -> Result<ExecResult, OrmError> {
432 Ok(ExecResult::default())
433 }
434 async fn fetch_raw(&self, _: &str, _: Vec<Value>) -> Result<QueryResult, OrmError> {
435 Ok(QueryResult::default())
436 }
437 async fn execute_raw(&self, _: &str, _: Vec<Value>) -> Result<u64, OrmError> {
438 Ok(0)
439 }
440 async fn execute_script(&self, _: &str) -> Result<(), OrmError> {
441 Ok(())
442 }
443 }
444
445 #[async_trait]
446 impl Backend for Stub {
447 async fn begin(
448 &self,
449 _: Option<crate::capabilities::IsolationLevel>,
450 ) -> Result<Box<dyn Transaction>, OrmError> {
451 Ok(Box::new(Stub { _id: 0 }))
452 }
453 }
454
455 #[async_trait]
456 impl Transaction for Stub {
457 async fn commit(&self) -> Result<(), OrmError> {
458 Ok(())
459 }
460 async fn rollback(&self) -> Result<(), OrmError> {
461 Ok(())
462 }
463 }
464
465 fn stub_db() -> Db {
466 Db::new(Stub { _id: 1 })
467 }
468
469 #[derive(Debug, Default, Clone)]
471 struct Captured {
472 name: String,
473 level: Option<tracing::Level>,
474 fields: Vec<(String, String)>,
475 }
476
477 impl Captured {
478 fn field(&self, name: &str) -> Option<&str> {
479 self.fields
480 .iter()
481 .rev()
482 .find(|(n, _)| n == name)
483 .map(|(_, v)| v.as_str())
484 }
485 }
486
487 struct Collect<'a>(&'a mut Vec<(String, String)>);
488
489 impl Visit for Collect<'_> {
490 fn record_debug(&mut self, field: &Field, value: &dyn std::fmt::Debug) {
491 self.0.push((field.name().to_owned(), format!("{value:?}")));
492 }
493 fn record_str(&mut self, field: &Field, value: &str) {
494 self.0.push((field.name().to_owned(), value.to_owned()));
495 }
496 }
497
498 #[derive(Clone, Default)]
499 struct Recorder {
500 spans: Arc<Mutex<Vec<(Id, Captured)>>>,
501 events: Arc<Mutex<Vec<Captured>>>,
502 }
503
504 impl<S: Subscriber + for<'a> LookupSpan<'a>> Layer<S> for Recorder {
505 fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, _: Context<'_, S>) {
506 let mut captured = Captured {
507 name: attrs.metadata().name().to_owned(),
508 level: Some(*attrs.metadata().level()),
509 fields: Vec::new(),
510 };
511 attrs.record(&mut Collect(&mut captured.fields));
512 self.spans.lock().unwrap().push((id.clone(), captured));
513 }
514 fn on_record(&self, id: &Id, values: &Record<'_>, _: Context<'_, S>) {
515 let mut spans = self.spans.lock().unwrap();
516 if let Some((_, captured)) = spans.iter_mut().find(|(i, _)| i == id) {
517 values.record(&mut Collect(&mut captured.fields));
518 }
519 }
520 fn on_event(&self, event: &Event<'_>, _: Context<'_, S>) {
521 let mut captured = Captured {
522 name: event.metadata().name().to_owned(),
523 level: Some(*event.metadata().level()),
524 fields: Vec::new(),
525 };
526 event.record(&mut Collect(&mut captured.fields));
527 self.events.lock().unwrap().push(captured);
528 }
529 }
530
531 fn recording() -> (Recorder, tracing::subscriber::DefaultGuard) {
532 let recorder = Recorder::default();
533 let subscriber = tracing_subscriber::registry().with(recorder.clone());
534 (recorder, tracing::subscriber::set_default(subscriber))
535 }
536
537 fn query_spans(recorder: &Recorder) -> Vec<Captured> {
538 recorder
539 .spans
540 .lock()
541 .unwrap()
542 .iter()
543 .map(|(_, c)| c.clone())
544 .filter(|c| c.name == "orm.query")
545 .collect()
546 }
547
548 #[tokio::test]
549 async fn every_entry_point_runs_in_an_orm_query_span() {
550 let (recorder, _guard) = recording();
551 let db = stub_db();
552 db.fetch(&QueryPlan::from_table("books")).await.unwrap();
553 db.execute(&WritePlan::Delete(DeletePlan {
554 table: "authors".into(),
555 filter: None,
556 returning: Vec::new(),
557 }))
558 .await
559 .unwrap();
560 db.raw_sql("SELECT 1", Vec::new()).await.unwrap();
561 db.raw_execute("DELETE FROM t", Vec::new()).await.unwrap();
562 db.execute_script("CREATE TABLE t (id INTEGER)")
563 .await
564 .unwrap();
565
566 let spans = query_spans(&recorder);
567 let seen: Vec<(&str, &str)> = spans
568 .iter()
569 .map(|s| {
570 (
571 s.field("db.operation").unwrap(),
572 s.field("db.table").unwrap(),
573 )
574 })
575 .collect();
576 assert_eq!(
577 seen,
578 [
579 ("select", "books"),
580 ("delete", "authors"),
581 ("raw", ""),
582 ("raw", ""),
583 ("script", "")
584 ]
585 );
586 for span in &spans {
587 assert_eq!(span.field("db.system"), Some("sqlite"));
588 assert_eq!(span.level, Some(tracing::Level::DEBUG));
589 let elapsed: f64 = span.field("elapsed_ms").unwrap().parse().unwrap();
590 assert!(elapsed >= 0.0);
591 }
592 }
593
594 #[tokio::test]
595 async fn raw_sql_is_traced_but_bind_parameters_never_are() {
596 let (recorder, _guard) = recording();
597 let db = stub_db();
598 db.raw_sql(
599 "SELECT * FROM users WHERE token = ?",
600 vec![Value::from("s3cr3t-token")],
601 )
602 .await
603 .unwrap();
604
605 let events = recorder.events.lock().unwrap();
606 let sql_event = events
607 .iter()
608 .find(|e| e.field("sql").is_some())
609 .expect("sql event");
610 assert_eq!(sql_event.level, Some(tracing::Level::TRACE));
611 assert_eq!(
612 sql_event.field("sql"),
613 Some("SELECT * FROM users WHERE token = ?")
614 );
615 let everything = format!("{events:?} {:?}", query_spans(&recorder));
616 assert!(!everything.contains("s3cr3t-token"), "{everything}");
617 }
618
619 #[tokio::test]
620 async fn plan_calls_never_emit_sql_or_values() {
621 let (recorder, _guard) = recording();
622 let db = stub_db();
623 db.fetch(&QueryPlan::from_table("books")).await.unwrap();
624 assert!(recorder.events.lock().unwrap().is_empty());
625 }
626
627 #[tokio::test]
628 async fn same_database_follows_the_pool_across_clones_and_transactions() {
629 let (a, b) = (stub_db(), stub_db());
630 assert!(a.same_database(&a.clone()));
631 assert!(!a.same_database(&b));
632 let (outer, other) = (a.clone(), b.clone());
633 a.transaction(|tx| async move {
634 assert!(tx.same_database(&outer));
635 assert!(!tx.same_database(&other));
636 Ok::<_, OrmError>(())
637 })
638 .await
639 .unwrap();
640 }
641
642 #[tokio::test]
643 async fn signals_are_shared_by_clones_and_transaction_handles() {
644 let signals = Signals::new();
645 let db = stub_db().with_signals(signals.clone());
646 assert!(db.clone().signals().is_empty());
647 db.transaction(|tx| async move {
648 let seen = tx.signals().clone();
650 tx.transaction(|nested| async move {
651 assert_eq!(nested.signals().len(), seen.len());
652 Ok::<_, OrmError>(())
653 })
654 .await
655 })
656 .await
657 .unwrap();
658 }
659
660 #[test]
661 fn databases_lists_sorted_aliases_and_hides_the_router_in_debug() {
662 let dbs = Databases::new()
663 .with("replica", stub_db())
664 .with("default", stub_db())
665 .with("analytics", stub_db());
666 assert_eq!(
667 dbs.aliases().collect::<Vec<_>>(),
668 ["analytics", "default", "replica"]
669 );
670 assert!(dbs.default_db().is_some());
671 assert!(dbs.get("missing").is_none());
672 assert!(format!("{dbs:?}").contains("analytics"));
673 }
674}