sea_orm/database/
proxy.rs1use crate::{error::*, ExecResult, ExecResultHolder, QueryResult, QueryResultRow, Statement};
2
3use sea_query::{Value, ValueType};
4use std::{collections::BTreeMap, fmt::Debug};
5
6#[async_trait::async_trait]
8pub trait ProxyDatabaseTrait: Send + Sync + std::fmt::Debug {
9 async fn query(&self, statement: Statement) -> Result<Vec<ProxyRow>, DbErr>;
11
12 async fn execute(&self, statement: Statement) -> Result<ProxyExecResult, DbErr>;
14
15 async fn begin(&self) {}
17
18 async fn commit(&self) {}
20
21 async fn rollback(&self) {}
23
24 fn start_rollback(&self) {}
26
27 async fn ping(&self) -> Result<(), DbErr> {
29 Ok(())
30 }
31}
32
33#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
35pub struct ProxyExecResult {
36 pub last_insert_id: u64,
38 pub rows_affected: u64,
40}
41
42impl ProxyExecResult {
43 pub fn new(last_insert_id: u64, rows_affected: u64) -> Self {
45 Self {
46 last_insert_id,
47 rows_affected,
48 }
49 }
50}
51
52impl Default for ExecResultHolder {
53 fn default() -> Self {
54 Self::Proxy(ProxyExecResult::default())
55 }
56}
57
58impl From<ProxyExecResult> for ExecResult {
59 fn from(result: ProxyExecResult) -> Self {
60 Self {
61 result: ExecResultHolder::Proxy(result),
62 }
63 }
64}
65
66impl From<ExecResult> for ProxyExecResult {
67 fn from(result: ExecResult) -> Self {
68 match result.result {
69 #[cfg(feature = "sqlx-mysql")]
70 ExecResultHolder::SqlxMySql(result) => Self {
71 last_insert_id: result.last_insert_id(),
72 rows_affected: result.rows_affected(),
73 },
74 #[cfg(feature = "sqlx-postgres")]
75 ExecResultHolder::SqlxPostgres(result) => Self {
76 last_insert_id: 0,
77 rows_affected: result.rows_affected(),
78 },
79 #[cfg(feature = "sqlx-sqlite")]
80 ExecResultHolder::SqlxSqlite(result) => Self {
81 last_insert_id: result.last_insert_rowid() as u64,
82 rows_affected: result.rows_affected(),
83 },
84 #[cfg(feature = "mock")]
85 ExecResultHolder::Mock(result) => Self {
86 last_insert_id: result.last_insert_id,
87 rows_affected: result.rows_affected,
88 },
89 ExecResultHolder::Proxy(result) => result,
90 }
91 }
92}
93
94#[derive(Clone, Debug, Default)]
97pub struct ProxyRow {
98 pub values: BTreeMap<String, Value>,
100}
101
102impl ProxyRow {
103 pub fn new(values: BTreeMap<String, Value>) -> Self {
105 Self { values }
106 }
107}
108
109impl From<BTreeMap<String, Value>> for ProxyRow {
110 fn from(values: BTreeMap<String, Value>) -> Self {
111 Self { values }
112 }
113}
114
115impl From<ProxyRow> for BTreeMap<String, Value> {
116 fn from(row: ProxyRow) -> Self {
117 row.values
118 }
119}
120
121impl From<ProxyRow> for Vec<(String, Value)> {
122 fn from(row: ProxyRow) -> Self {
123 row.values.into_iter().collect()
124 }
125}
126
127impl From<ProxyRow> for QueryResult {
128 fn from(row: ProxyRow) -> Self {
129 QueryResult {
130 row: QueryResultRow::Proxy(row),
131 }
132 }
133}
134
135#[cfg(feature = "with-json")]
136impl From<ProxyRow> for serde_json::Value {
137 fn from(val: ProxyRow) -> serde_json::Value {
138 val.values
139 .into_iter()
140 .map(|(k, v)| (k, sea_query::sea_value_to_json_value(&v)))
141 .collect()
142 }
143}
144
145pub fn from_query_result_to_proxy_row(result: &QueryResult) -> ProxyRow {
147 match &result.row {
148 #[cfg(feature = "sqlx-mysql")]
149 QueryResultRow::SqlxMySql(row) => crate::from_sqlx_mysql_row_to_proxy_row(row),
150 #[cfg(feature = "sqlx-postgres")]
151 QueryResultRow::SqlxPostgres(row) => crate::from_sqlx_postgres_row_to_proxy_row(row),
152 #[cfg(feature = "sqlx-sqlite")]
153 QueryResultRow::SqlxSqlite(row) => crate::from_sqlx_sqlite_row_to_proxy_row(row),
154 #[cfg(feature = "mock")]
155 QueryResultRow::Mock(row) => ProxyRow {
156 values: row.values.clone(),
157 },
158 QueryResultRow::Proxy(row) => row.to_owned(),
159 }
160}
161
162impl ProxyRow {
163 pub fn try_get<T, I: crate::ColIdx>(&self, index: I) -> Result<T, DbErr>
165 where
166 T: ValueType,
167 {
168 if let Some(index) = index.as_str() {
169 T::try_from(
170 self.values
171 .get(index)
172 .ok_or_else(|| query_err(format!("No column for ColIdx {index:?}")))?
173 .clone(),
174 )
175 .map_err(type_err)
176 } else if let Some(index) = index.as_usize() {
177 let (_, value) = self
178 .values
179 .iter()
180 .nth(*index)
181 .ok_or_else(|| query_err(format!("Column at index {index} not found")))?;
182 T::try_from(value.clone()).map_err(type_err)
183 } else {
184 unreachable!("Missing ColIdx implementation for ProxyRow");
185 }
186 }
187
188 pub fn into_column_value_tuples(self) -> impl Iterator<Item = (String, Value)> {
190 self.values.into_iter()
191 }
192}
193
194#[cfg(test)]
195mod tests {
196 use crate::{
197 entity::*, tests_cfg::*, Database, DbBackend, DbErr, ProxyDatabaseTrait, ProxyExecResult,
198 ProxyRow, Statement,
199 };
200 use std::sync::{Arc, Mutex};
201
202 #[derive(Debug)]
203 struct ProxyDb {}
204
205 impl ProxyDatabaseTrait for ProxyDb {
206 async fn query(&self, statement: Statement) -> Result<Vec<ProxyRow>, DbErr> {
207 println!("SQL query: {}", statement.sql);
208 Ok(vec![].into())
209 }
210
211 async fn execute(&self, statement: Statement) -> Result<ProxyExecResult, DbErr> {
212 println!("SQL execute: {}", statement.sql);
213 Ok(ProxyExecResult {
214 last_insert_id: 1,
215 rows_affected: 1,
216 })
217 }
218 }
219
220 #[smol_potat::test]
221 async fn create_proxy_conn() {
222 let _db = Database::connect_proxy(DbBackend::MySql, Arc::new(Box::new(ProxyDb {})))
223 .await
224 .unwrap();
225 }
226
227 #[smol_potat::test]
228 async fn select_rows() {
229 let db = Database::connect_proxy(DbBackend::MySql, Arc::new(Box::new(ProxyDb {})))
230 .await
231 .unwrap();
232
233 let _ = cake::Entity::find().all(&db).await;
234 }
235
236 #[smol_potat::test]
237 async fn insert_one_row() {
238 let db = Database::connect_proxy(DbBackend::MySql, Arc::new(Box::new(ProxyDb {})))
239 .await
240 .unwrap();
241
242 let item = cake::ActiveModel {
243 id: NotSet,
244 name: Set("Alice".to_string()),
245 };
246
247 cake::Entity::insert(item).exec(&db).await.unwrap();
248 }
249}