1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
//! Database backend abstraction
use async_trait::async_trait;
use super::{
error::Result,
types::{DatabaseType, IsolationLevel, QueryResult, QueryValue, Row, TransactionExecutor},
};
/// Core database backend trait
#[async_trait]
pub trait DatabaseBackend: Send + Sync {
/// Returns the database type
fn database_type(&self) -> DatabaseType;
/// Generates a placeholder for the given parameter index (1-based)
fn placeholder(&self, index: usize) -> String;
/// Returns whether the database supports RETURNING clause
fn supports_returning(&self) -> bool;
/// Returns whether the database supports ON CONFLICT clause
fn supports_on_conflict(&self) -> bool;
/// Returns whether DDL statements can be rolled back in transactions
///
/// PostgreSQL and SQLite support transactional DDL - CREATE TABLE, etc.
/// can be rolled back if the transaction fails.
///
/// MySQL/MariaDB do NOT support transactional DDL - DDL statements cause
/// an implicit commit, so they cannot be rolled back.
fn supports_transactional_ddl(&self) -> bool {
// Default implementation: check database type
self.database_type().supports_transactional_ddl()
}
/// Executes a query that modifies the database
async fn execute(&self, sql: &str, params: Vec<QueryValue>) -> Result<QueryResult>;
/// Fetches a single row from the database
async fn fetch_one(&self, sql: &str, params: Vec<QueryValue>) -> Result<Row>;
/// Fetches all matching rows from the database
async fn fetch_all(&self, sql: &str, params: Vec<QueryValue>) -> Result<Vec<Row>>;
/// Fetches an optional single row from the database
async fn fetch_optional(&self, sql: &str, params: Vec<QueryValue>) -> Result<Option<Row>>;
/// Begin a database transaction and return a dedicated executor
///
/// This method acquires a dedicated database connection and begins a
/// transaction on it. All queries executed through the returned
/// `TransactionExecutor` are guaranteed to run on the same physical
/// connection, ensuring proper transaction isolation.
///
/// # Returns
///
/// A boxed `TransactionExecutor` that holds the dedicated connection
/// and provides methods for executing queries within the transaction.
async fn begin(&self) -> Result<Box<dyn TransactionExecutor>>;
/// Begin a database transaction with a specific isolation level
///
/// This method is similar to `begin()`, but allows specifying the
/// transaction isolation level. The isolation level controls the
/// visibility of changes made by other concurrent transactions.
///
/// # Arguments
///
/// * `isolation_level` - The desired isolation level for the transaction
///
/// # Returns
///
/// A boxed `TransactionExecutor` that holds the dedicated connection
/// with the specified isolation level.
///
/// # Default Implementation
///
/// Falls back to `begin()` with the database's default isolation level.
/// Backends that support custom isolation levels should override this.
async fn begin_with_isolation(
&self,
isolation_level: IsolationLevel,
) -> Result<Box<dyn TransactionExecutor>> {
let _ = isolation_level;
// Default implementation: ignore isolation level and use default
self.begin().await
}
/// Returns self as &dyn std::any::Any for downcasting
fn as_any(&self) -> &dyn std::any::Any;
}