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