Skip to main content

systemprompt_database/services/
provider.rs

1//! Trait abstractions over a database backend.
2//!
3//! [`DatabaseProvider`] is dyn-safe (callers hold `Arc<dyn DatabaseProvider>`)
4//! and uses `#[async_trait]`. [`DatabaseProviderExt`] is generic, never used
5//! through a trait object, and uses native `async fn`.
6
7use crate::error::DatabaseResult;
8use crate::models::{
9    DatabaseInfo, DatabaseTransaction, DbValue, FromDatabaseRow, JsonRow, QueryResult,
10    QuerySelector, ToDbValue,
11};
12use async_trait::async_trait;
13use std::sync::Arc;
14
15#[async_trait]
16pub trait DatabaseProvider: Send + Sync + std::fmt::Debug {
17    fn get_postgres_pool(&self) -> Option<Arc<sqlx::PgPool>> {
18        None
19    }
20
21    fn is_postgres(&self) -> bool {
22        true
23    }
24
25    async fn execute(
26        &self,
27        query: &dyn QuerySelector,
28        params: &[&dyn ToDbValue],
29    ) -> DatabaseResult<u64>;
30
31    async fn execute_raw(&self, sql: &str) -> DatabaseResult<()>;
32
33    async fn fetch_all(
34        &self,
35        query: &dyn QuerySelector,
36        params: &[&dyn ToDbValue],
37    ) -> DatabaseResult<Vec<JsonRow>>;
38
39    async fn fetch_one(
40        &self,
41        query: &dyn QuerySelector,
42        params: &[&dyn ToDbValue],
43    ) -> DatabaseResult<JsonRow>;
44
45    async fn fetch_optional(
46        &self,
47        query: &dyn QuerySelector,
48        params: &[&dyn ToDbValue],
49    ) -> DatabaseResult<Option<JsonRow>>;
50
51    async fn fetch_scalar_value(
52        &self,
53        query: &dyn QuerySelector,
54        params: &[&dyn ToDbValue],
55    ) -> DatabaseResult<DbValue>;
56
57    async fn begin_transaction(&self) -> DatabaseResult<Box<dyn DatabaseTransaction>>;
58
59    async fn get_database_info(&self) -> DatabaseResult<DatabaseInfo>;
60
61    async fn test_connection(&self) -> DatabaseResult<()>;
62
63    async fn execute_batch(&self, sql: &str) -> DatabaseResult<()>;
64
65    async fn query_raw(&self, query: &dyn QuerySelector) -> DatabaseResult<QueryResult>;
66
67    async fn query_raw_with(
68        &self,
69        query: &dyn QuerySelector,
70        params: &[&dyn ToDbValue],
71    ) -> DatabaseResult<QueryResult>;
72}
73
74#[expect(
75    async_fn_in_trait,
76    reason = "internal extension trait used statically; no dyn dispatch, so no Send-bound concern"
77)]
78pub trait DatabaseProviderExt {
79    async fn fetch_typed_optional<T: FromDatabaseRow>(
80        &self,
81        query: &dyn QuerySelector,
82        params: &[&dyn ToDbValue],
83    ) -> DatabaseResult<Option<T>>;
84
85    async fn fetch_typed_one<T: FromDatabaseRow>(
86        &self,
87        query: &dyn QuerySelector,
88        params: &[&dyn ToDbValue],
89    ) -> DatabaseResult<T>;
90
91    async fn fetch_typed_all<T: FromDatabaseRow>(
92        &self,
93        query: &dyn QuerySelector,
94        params: &[&dyn ToDbValue],
95    ) -> DatabaseResult<Vec<T>>;
96}