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