1use crate::{
2 FfiTransaction,
3 backend::FfiBackend,
4 db_err::FfiDbErr,
5 exec_result::FfiExecResult,
6 proxy_row::{FfiOptionalProxyRow, FfiProxyRow},
7 result::FfiResult,
8 statement::FfiStatement,
9 string::StringPtr,
10 vec::VecPtr
11};
12use async_compat::Compat;
13use async_ffi::{BorrowingFfiFuture, FutureExt as _};
14use async_trait::async_trait;
15use futures_util::future::{BoxFuture, FutureExt as _};
16use sea_orm::{
17 ConnectionTrait, DatabaseBackend, DatabaseTransaction, DbErr, ExecResult,
18 QueryResult, Statement, TransactionTrait
19};
20use std::{ffi::c_void, future::Future};
21
22macro_rules! doc_safety {
24 () => {
25 concat!(
26 "\n",
27 "# Safety\n",
28 "\n",
29 "This function is only safe to be called on the same side of the FFI ",
30 "boundary that was used to construct the connection pointer using the ",
31 "[`new`](ConnectionPtr::new) function, or any binary that was compiled ",
32 "using the exact same dependencies and the exact same compiler and linker."
33 )
34 };
35}
36
37pub trait SeaOrmConnection: ConnectionTrait + Send + Sync {
40 fn begin(&self) -> BoxFuture<'_, Result<DatabaseTransaction, DbErr>>;
41}
42
43impl<T> SeaOrmConnection for T
44where
45 T: ConnectionTrait + TransactionTrait + Send + Sync
46{
47 fn begin(&self) -> BoxFuture<'_, Result<DatabaseTransaction, DbErr>> {
48 TransactionTrait::begin(self)
49 }
50}
51
52struct BoxedConnection(Box<dyn SeaOrmConnection + Send + Sync>);
67
68#[repr(transparent)]
73struct ConnectionPtr(*mut c_void);
74
75impl ConnectionPtr {
76 fn new(inner: Box<dyn SeaOrmConnection + Send + Sync>) -> Self {
77 Self(Box::into_raw(Box::new(BoxedConnection(inner))) as _)
78 }
79
80 #[doc = doc_safety!()]
82 unsafe fn get(&self) -> &(dyn SeaOrmConnection + Send + Sync) {
83 let ptr: *mut BoxedConnection = self.0.cast();
84 &*(&*ptr).0
85 }
86
87 #[doc = doc_safety!()]
89 #[allow(clippy::wrong_self_convention)] unsafe fn into_inner(&mut self) -> Box<dyn SeaOrmConnection + Send + Sync> {
91 let ptr: *mut BoxedConnection = self.0.cast();
92 (*Box::from_raw(ptr)).0
93 }
94}
95
96type DropConnection = extern "C" fn(&mut ConnectionPtr);
98
99#[doc = doc_safety!()]
101extern "C" fn drop_connection(ptr: &mut ConnectionPtr) {
102 drop(unsafe { ptr.into_inner() });
103}
104
105type BeginTransaction =
109 extern "C" fn(
110 &ConnectionPtr
111 ) -> BorrowingFfiFuture<'_, FfiResult<FfiTransaction, FfiDbErr>>;
112
113#[doc = doc_safety!()]
115extern "C" fn begin_transaction(
116 ptr: &ConnectionPtr
117) -> BorrowingFfiFuture<'_, FfiResult<FfiTransaction, FfiDbErr>> {
118 let conn = unsafe { ptr.get() };
119 Compat::new(async move {
120 conn.begin()
121 .await
122 .map(|transaction| FfiTransaction::new(Box::new(transaction)))
123 .map_err(Into::into)
124 .into()
125 })
126 .into_ffi()
127}
128
129type GetDatabaseBackend<Ptr> = extern "C" fn(&Ptr) -> FfiBackend;
133
134#[doc = doc_safety!()]
136extern "C" fn get_database_backend(ptr: &ConnectionPtr) -> FfiBackend {
137 unsafe { ptr.get() }.get_database_backend().into()
138}
139
140type Execute<Ptr> =
144 extern "C" fn(
145 &Ptr,
146 FfiStatement
147 ) -> BorrowingFfiFuture<'_, FfiResult<FfiExecResult, FfiDbErr>>;
148
149pub(crate) fn execute_impl<C>(
151 conn: &C,
152 stmt: FfiStatement
153) -> BorrowingFfiFuture<'_, FfiResult<FfiExecResult, FfiDbErr>>
154where
155 C: ConnectionTrait + Send + Sync + ?Sized
156{
157 Compat::new(async move {
158 conn.execute(stmt.into())
159 .await
160 .map(|exec_result| {
161 FfiExecResult::new(conn.get_database_backend(), exec_result)
162 })
163 .map_err(Into::into)
164 .into()
165 })
166 .into_ffi()
167}
168
169#[doc = doc_safety!()]
171extern "C" fn execute(
172 ptr: &ConnectionPtr,
173 stmt: FfiStatement
174) -> BorrowingFfiFuture<'_, FfiResult<FfiExecResult, FfiDbErr>> {
175 execute_impl(unsafe { ptr.get() }, stmt)
176}
177
178type ExecuteUnprepared<Ptr> =
182 extern "C" fn(
183 &Ptr,
184 StringPtr
185 ) -> BorrowingFfiFuture<'_, FfiResult<FfiExecResult, FfiDbErr>>;
186
187pub(crate) fn execute_unprepared_impl<C>(
189 conn: &C,
190 sql: StringPtr
191) -> BorrowingFfiFuture<'_, FfiResult<FfiExecResult, FfiDbErr>>
192where
193 C: ConnectionTrait + Send + Sync + ?Sized
194{
195 Compat::new(async move {
196 conn.execute_unprepared(sql.as_str())
197 .await
198 .map(|exec_result| {
199 FfiExecResult::new(conn.get_database_backend(), exec_result)
200 })
201 .map_err(Into::into)
202 .into()
203 })
204 .into_ffi()
205}
206
207#[doc = doc_safety!()]
209extern "C" fn execute_unprepared(
210 ptr: &ConnectionPtr,
211 sql: StringPtr
212) -> BorrowingFfiFuture<'_, FfiResult<FfiExecResult, FfiDbErr>> {
213 execute_unprepared_impl(unsafe { ptr.get() }, sql)
214}
215
216type QueryOne<Ptr> =
220 extern "C" fn(
221 &Ptr,
222 FfiStatement
223 ) -> BorrowingFfiFuture<'_, FfiResult<FfiOptionalProxyRow, FfiDbErr>>;
224
225pub(crate) fn query_one_impl<C>(
227 conn: &C,
228 stmt: FfiStatement
229) -> BorrowingFfiFuture<'_, FfiResult<FfiOptionalProxyRow, FfiDbErr>>
230where
231 C: ConnectionTrait + Send + Sync + ?Sized
232{
233 Compat::new(async move {
234 conn.query_one(stmt.into())
235 .await
236 .map(Into::into)
237 .map_err(Into::into)
238 .into()
239 })
240 .into_ffi()
241}
242
243#[doc = doc_safety!()]
245extern "C" fn query_one(
246 ptr: &ConnectionPtr,
247 stmt: FfiStatement
248) -> BorrowingFfiFuture<'_, FfiResult<FfiOptionalProxyRow, FfiDbErr>> {
249 query_one_impl(unsafe { ptr.get() }, stmt)
250}
251
252type QueryAll<Ptr> =
256 extern "C" fn(
257 &Ptr,
258 FfiStatement
259 ) -> BorrowingFfiFuture<'_, FfiResult<VecPtr<FfiProxyRow>, FfiDbErr>>;
260
261pub(crate) fn query_all_impl<C>(
263 conn: &C,
264 stmt: FfiStatement
265) -> BorrowingFfiFuture<'_, FfiResult<VecPtr<FfiProxyRow>, FfiDbErr>>
266where
267 C: ConnectionTrait + Send + Sync + ?Sized
268{
269 Compat::new(async move {
270 conn.query_all(stmt.into())
271 .await
272 .map(|rows| {
273 rows.into_iter()
274 .map(Into::into)
275 .collect::<Vec<FfiProxyRow>>()
276 .into()
277 })
278 .map_err(Into::into)
279 .into()
280 })
281 .into_ffi()
282}
283
284#[doc = doc_safety!()]
286extern "C" fn query_all(
287 ptr: &ConnectionPtr,
288 stmt: FfiStatement
289) -> BorrowingFfiFuture<'_, FfiResult<VecPtr<FfiProxyRow>, FfiDbErr>> {
290 query_all_impl(unsafe { ptr.get() }, stmt)
291}
292
293type IsMockConnection<Ptr> = extern "C" fn(&Ptr) -> bool;
297
298#[doc = doc_safety!()]
300extern "C" fn is_mock_connection(ptr: &ConnectionPtr) -> bool {
301 unsafe { ptr.get() }.is_mock_connection()
302}
303
304#[repr(C)]
305pub(crate) struct ConnectionVTable<Ptr> {
306 pub(crate) backend: GetDatabaseBackend<Ptr>,
307 pub(crate) execute: Execute<Ptr>,
308 pub(crate) execute_unprepared: ExecuteUnprepared<Ptr>,
309 pub(crate) query_one: QueryOne<Ptr>,
310 pub(crate) query_all: QueryAll<Ptr>,
311 pub(crate) is_mock_connection: IsMockConnection<Ptr>
312}
313
314#[repr(C)]
318pub struct FfiConnection {
319 ptr: ConnectionPtr,
320 drop: DropConnection,
321 begin: BeginTransaction,
322 vtable: ConnectionVTable<ConnectionPtr>
323}
324
325impl FfiConnection {
326 pub fn new(inner: Box<dyn SeaOrmConnection + Send + Sync>) -> Self {
327 Self {
328 ptr: ConnectionPtr::new(inner),
329 drop: drop_connection,
330 begin: begin_transaction,
331 vtable: ConnectionVTable {
332 backend: get_database_backend,
333 execute,
334 execute_unprepared,
335 query_one,
336 query_all,
337 is_mock_connection
338 }
339 }
340 }
341
342 pub fn begin(
343 &self
344 ) -> impl Future<Output = Result<FfiTransaction, DbErr>> + Send + '_ {
345 (self.begin)(&self.ptr).map(|res| res.into_result().map_err(Into::into))
346 }
347}
348
349impl Drop for FfiConnection {
350 fn drop(&mut self) {
351 (self.drop)(&mut self.ptr)
352 }
353}
354
355unsafe impl Send for ConnectionPtr {}
358unsafe impl Sync for ConnectionPtr {}
359unsafe impl<Ptr> Send for ConnectionVTable<Ptr> {}
360unsafe impl<Ptr> Sync for ConnectionVTable<Ptr> {}
361unsafe impl Send for FfiConnection {}
362unsafe impl Sync for FfiConnection {}
363
364impl<Ptr> ConnectionVTable<Ptr> {
365 pub(crate) fn get_database_backend(&self, ptr: &Ptr) -> DatabaseBackend {
366 (self.backend)(ptr).into()
367 }
368
369 pub(crate) async fn execute(
370 &self,
371 ptr: &Ptr,
372 stmt: Statement
373 ) -> Result<ExecResult, DbErr> {
374 (self.execute)(ptr, stmt.into())
375 .await
376 .map(Into::into)
377 .map_err(Into::into)
378 .into()
379 }
380
381 pub(crate) async fn execute_unprepared(
382 &self,
383 ptr: &Ptr,
384 sql: &str
385 ) -> Result<ExecResult, DbErr> {
386 let sql: String = sql.into();
389
390 (self.execute_unprepared)(ptr, sql.into())
391 .await
392 .map(Into::into)
393 .map_err(Into::into)
394 .into()
395 }
396
397 pub(crate) async fn query_one(
398 &self,
399 ptr: &Ptr,
400 stmt: Statement
401 ) -> Result<Option<QueryResult>, DbErr> {
402 (self.query_one)(ptr, stmt.into())
403 .await
404 .map(Into::into)
405 .map_err(Into::into)
406 .into()
407 }
408
409 pub(crate) async fn query_all(
410 &self,
411 ptr: &Ptr,
412 stmt: Statement
413 ) -> Result<Vec<QueryResult>, DbErr> {
414 (self.query_all)(ptr, stmt.into())
415 .await
416 .map(|rows| rows.into_vec().into_iter().map(Into::into).collect())
417 .map_err(Into::into)
418 .into()
419 }
420
421 pub(crate) fn is_mock_connection(&self, ptr: &Ptr) -> bool {
422 (self.is_mock_connection)(ptr)
423 }
424}
425
426#[async_trait]
427impl ConnectionTrait for FfiConnection {
428 fn get_database_backend(&self) -> DatabaseBackend {
429 self.vtable.get_database_backend(&self.ptr)
430 }
431
432 async fn execute(&self, stmt: Statement) -> Result<ExecResult, DbErr> {
433 self.vtable.execute(&self.ptr, stmt).await
434 }
435
436 async fn execute_unprepared(&self, sql: &str) -> Result<ExecResult, DbErr> {
437 self.vtable.execute_unprepared(&self.ptr, sql).await
438 }
439
440 async fn query_one(&self, stmt: Statement) -> Result<Option<QueryResult>, DbErr> {
441 self.vtable.query_one(&self.ptr, stmt).await
442 }
443
444 async fn query_all(&self, stmt: Statement) -> Result<Vec<QueryResult>, DbErr> {
445 self.vtable.query_all(&self.ptr, stmt).await
446 }
447
448 fn is_mock_connection(&self) -> bool {
449 self.vtable.is_mock_connection(&self.ptr)
450 }
451}