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() as u64,
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)]
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 Default for ProxyRow {
110 fn default() -> Self {
111 Self {
112 values: BTreeMap::new(),
113 }
114 }
115}
116
117impl From<BTreeMap<String, Value>> for ProxyRow {
118 fn from(values: BTreeMap<String, Value>) -> Self {
119 Self { values }
120 }
121}
122
123impl From<ProxyRow> for BTreeMap<String, Value> {
124 fn from(row: ProxyRow) -> Self {
125 row.values
126 }
127}
128
129impl From<ProxyRow> for Vec<(String, Value)> {
130 fn from(row: ProxyRow) -> Self {
131 row.values.into_iter().collect()
132 }
133}
134
135impl From<ProxyRow> for QueryResult {
136 fn from(row: ProxyRow) -> Self {
137 QueryResult {
138 row: QueryResultRow::Proxy(row),
139 }
140 }
141}
142
143#[cfg(feature = "with-json")]
144impl Into<serde_json::Value> for ProxyRow {
145 fn into(self) -> serde_json::Value {
146 self.values
147 .into_iter()
148 .map(|(k, v)| (k, sea_query::sea_value_to_json_value(&v)))
149 .collect()
150 }
151}
152
153pub fn from_query_result_to_proxy_row(result: &QueryResult) -> ProxyRow {
155 match &result.row {
156 #[cfg(feature = "sqlx-mysql")]
157 QueryResultRow::SqlxMySql(row) => crate::from_sqlx_mysql_row_to_proxy_row(&row),
158 #[cfg(feature = "sqlx-postgres")]
159 QueryResultRow::SqlxPostgres(row) => crate::from_sqlx_postgres_row_to_proxy_row(&row),
160 #[cfg(feature = "sqlx-sqlite")]
161 QueryResultRow::SqlxSqlite(row) => crate::from_sqlx_sqlite_row_to_proxy_row(&row),
162 #[cfg(feature = "mock")]
163 QueryResultRow::Mock(row) => ProxyRow {
164 values: row.values.clone(),
165 },
166 QueryResultRow::Proxy(row) => row.to_owned(),
167 }
168}
169
170impl ProxyRow {
171 pub fn try_get<T, I: crate::ColIdx>(&self, index: I) -> Result<T, DbErr>
173 where
174 T: ValueType,
175 {
176 if let Some(index) = index.as_str() {
177 T::try_from(
178 self.values
179 .get(index)
180 .ok_or_else(|| query_err(format!("No column for ColIdx {index:?}")))?
181 .clone(),
182 )
183 .map_err(type_err)
184 } else if let Some(index) = index.as_usize() {
185 let (_, value) = self
186 .values
187 .iter()
188 .nth(*index)
189 .ok_or_else(|| query_err(format!("Column at index {index} not found")))?;
190 T::try_from(value.clone()).map_err(type_err)
191 } else {
192 unreachable!("Missing ColIdx implementation for ProxyRow");
193 }
194 }
195
196 pub fn into_column_value_tuples(self) -> impl Iterator<Item = (String, Value)> {
198 self.values.into_iter()
199 }
200}
201
202#[cfg(test)]
203mod tests {
204 use crate::{
205 entity::*, tests_cfg::*, Database, DbBackend, DbErr, ProxyDatabaseTrait, ProxyExecResult,
206 ProxyRow, Statement,
207 };
208 use std::sync::{Arc, Mutex};
209
210 #[derive(Debug)]
211 struct ProxyDb {}
212
213 impl ProxyDatabaseTrait for ProxyDb {
214 async fn query(&self, statement: Statement) -> Result<Vec<ProxyRow>, DbErr> {
215 println!("SQL query: {}", statement.sql);
216 Ok(vec![].into())
217 }
218
219 async fn execute(&self, statement: Statement) -> Result<ProxyExecResult, DbErr> {
220 println!("SQL execute: {}", statement.sql);
221 Ok(ProxyExecResult {
222 last_insert_id: 1,
223 rows_affected: 1,
224 })
225 }
226 }
227
228 #[smol_potat::test]
229 async fn create_proxy_conn() {
230 let _db = Database::connect_proxy(DbBackend::MySql, Arc::new(Box::new(ProxyDb {})))
231 .await
232 .unwrap();
233 }
234
235 #[smol_potat::test]
236 async fn select_rows() {
237 let db = Database::connect_proxy(DbBackend::MySql, Arc::new(Box::new(ProxyDb {})))
238 .await
239 .unwrap();
240
241 let _ = cake::Entity::find().all(&db).await;
242 }
243
244 #[smol_potat::test]
245 async fn insert_one_row() {
246 let db = Database::connect_proxy(DbBackend::MySql, Arc::new(Box::new(ProxyDb {})))
247 .await
248 .unwrap();
249
250 let item = cake::ActiveModel {
251 id: NotSet,
252 name: Set("Alice".to_string()),
253 };
254
255 cake::Entity::insert(item).exec(&db).await.unwrap();
256 }
257}