# sea-orm-ffi  [](https://crates.io/crates/sea-orm-ffi) [](https://docs.rs/sea-orm-ffi) [](https://codeberg.org/proto-x/sea-orm-ffi)
This crate provides a compatibility layer for [Sea-ORM][__link0] when crossing a
Rust-to-Rust FFI boundary.
## Example
On the host side, you first create your database connection, which we can then
convert into an [`FfiConnection`][__link1]:
```rust
use async_ffi::BorrowingFfiFuture;
use sea_orm::DatabaseConnection;
use sea_orm_ffi::FfiConnection;
// See Sea-ORM's documentation on how to obtain a database connection
let conn: DatabaseConnection = todo!();
// Create FFI-safe connection wrapper.
// While this can be used on the host side as well, it is recommended you avoid this
// as it will likely be less performant.
// Therefore, you might want to wrap `conn` in an `Arc` to share it with the host.
let ffi_conn = FfiConnection::new(Box::new(conn));
// Obtain the plugin function that needs the database connection via libloading or similar
let plugin_function: Symbol<
extern "C" fn(&FfiConnection) -> BorrowingFfiFuture<'_, ()>
> = todo!();
// Call the plugin function
plugin_function(&ffi_conn).await;
```
On the plugin side, you can treat [`FfiConnection`][__link2] like any other database connection:
```rust
mod comment {
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, DeriveEntityModel)]
#[sea_orm(table_name = "comment")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i64,
pub author: String,
pub comment: String
}
#[derive(Debug, DeriveRelation, EnumIter)]
pub enum Relation {}
impl ActiveModelBehavior for ActiveModel {}
}
use comment::Entity as Comment;
use async_ffi::{BorrowingFfiFuture, FutureExt as _};
use sea_orm::EntityTrait as _;
use sea_orm_ffi::FfiConnection;
async fn print_comments(conn: &FfiConnection) {
let Ok(comments) = Comment::find().all(conn).await else {
eprintln!("Failed to load comments");
return;
};
for comment in comments {
println!("{comment:?}");
}
}
#[no_mangle]
extern "C" fn plugin_function(conn: &FfiConnection) -> BorrowingFfiFuture<'_, ()> {
print_comments(conn).into_ffi()
}
```
## How it works
Sea-ORM relies on two main traits for its database connection: [`ConnectionTrait`][__link3]
and [`TransactionTrait`][__link4]. The former one is really nice because it is *`dyn`
compatible*. This is also sometimes called *object safe*.
What that means is that we build pointers to the trait without knowing the specific
type. For example, we can write `&dyn ConnectionTrait` or `Box<dyn ConnectionTrait>`.
In order to pass things via an FFI boundary, we need a type with a stable ABI. ABI
stands for Application Binary Interface, and unfortunately Rust’s default ABI is not
guaranteed to be stable. We must therefore use another, stable ABI. Otherwise, it
would be inherently unsafe to load plugins compiled with a different Rust compiler
than the plugin host. Luckily, we can use a pointer to `Box<dyn ConnectionTrait>` to
pass it through an FFI boundary.
The only problem is that that pointer is not ABI stable, so the plugin must treat
this pointer as *opaque*, and only pass its value back to the plugin host. We
therefore create C-ABI functions in the plugin host that call the functions from
[`ConnectionTrait`][__link5] and pass those function pointers to the plugin alongside the
connection pointer. The plugin can then pass the connection pointer to these function
pointers to call the various functions.
But what about function arguments? And return types? Good question!
The most complicated type here is definitely [`Future`][__link6]. Luckily, the problem of
using async code with Rust-to-Rust FFI has been solved by [`async-ffi`][__link7]. Well,
almost. Because [`tokio`][__link8] uses some thread-local storage unavailable to plugins
loaded with [`libloading`][__link9], any attempt to use tokio-specific futures will panic.
The only runtimes supported by [`sea-orm`][__link10]/[`sqlx`][__link11] in the current release are
[`async-std`][__link12], which is deprecated, and [`tokio`][__link13]. We therefore rely on
[`async-compat`][__link14] to inject a tokio runtime, regardless of the executor that polls
the futures. In the future, when [`sqlx`][__link15] is released with support for [`smol`][__link16],
this can and will be removed.
The other types are fairly easy to bridge accross the FFI boundary. But, since none
of them are ABI stable by themselves, we have to convert them to and from ABI stable
types first. This is made possible by the `proxy` feature of [`sea-orm`][__link17] that allows
constructing our own database responses. We cannot use opaque pointers here as those
types are designed to be created on one side and read on the other side of the FFI
boundary.
But what about the [`TransactionTrait`][__link18]? Well, this one is unfortunately not *`dyn`
compatible*, which is the unfortunate side effect of it having a function that
accepts generic parameters. However, we can still bridge the functions that do not
take the generic parameters - namely the [`begin()`][__link19]
function. This should be sufficient to provide transaction support - but not via
the [`TransactionTrait`][__link20]. While it would be possible to write the
[`transaction()`][__link21] method on the plugin side,
there is no way to create a [`DatabaseTransaction`][__link22] from any ABI stable type. Instead,
the [`FfiConnection::begin()`][__link23] function returns a [`FfiTransaction`][__link24], which again
implements [`ConnectionTrait`][__link25], so you can use it on the plugin side just like you
would use [`DatabaseTransaction`][__link26] on the host side.
## Features
[Sea-ORM][__link27] makes heavy usage of features, including for the [`Value`][__link28] enum
which is one of the types bridged by this crate. Therefore, all features that enable
types must not only be enable for [`sea-orm`][__link29] but also for this crate. At the time
of writing, [`sea-orm`][__link30] supports more types than we do. Feel free to open an [issue][__link31]
or [pull request][__link32] if you are missing a feature.
Note that there will not be a compile error when there is a feature mismatch. Instead,
any attempt to use types unsupported by this crate will result in a panic at runtime.
### Migrations
Additionally, we have a `refinery` feature that enables support for migrations using
the [`refinery`][__link33] crate. When this feature is enabled, [`FfiConnection`][__link34] implements
[`AsyncMigrate`][__link35] so you can call
[`Runner::run_async()`][__link36] with the connection.
## Changelog
Please see [releases][__link37] for a list of changes per release.
[__cargo_doc2readme_dependencies_info]: ggGkYW0CYXSEG9qP0jVqEegAG9_0_pNDgzx8G50Fu7kvw9l7G41RRbSvpQtwYXKEGy4gZqK7QmPLG82Og0liOyN8G5u4L4nnw-DjGyVr32fB8s1-YWSFgmxhc3luY19jb21wYXRlMC4yLjWCaWFzeW5jX2ZmaWUwLjUuMIJtcmVmaW5lcnlfY29yZWUwLjkuMINrc2VhLW9ybS1mZmllMC4xLjFrc2VhX29ybV9mZmmCZ3NlYV9vcm1mMS4xLjE5
[__link0]: https://crates.io/crates/sea_orm/1.1.19
[__link1]: https://docs.rs/sea-orm-ffi/0.1.1/sea_orm_ffi/?search=FfiConnection
[__link10]: https://crates.io/crates/sea_orm/1.1.19
[__link11]: https://docs.rs/sea_orm/1.1.19/sea_orm/?search=sqlx
[__link12]: https://crates.io/crates/async-std
[__link13]: https://crates.io/crates/tokio
[__link14]: https://crates.io/crates/async_compat/0.2.5
[__link15]: https://docs.rs/sea_orm/1.1.19/sea_orm/?search=sqlx
[__link16]: https://crates.io/crates/smol
[__link17]: https://crates.io/crates/sea_orm/1.1.19
[__link18]: https://docs.rs/sea_orm/1.1.19/sea_orm/?search=TransactionTrait
[__link19]: https://docs.rs/sea_orm/1.1.19/sea_orm/?search=TransactionTrait::begin
[__link2]: https://docs.rs/sea-orm-ffi/0.1.1/sea_orm_ffi/?search=FfiConnection
[__link20]: https://docs.rs/sea_orm/1.1.19/sea_orm/?search=TransactionTrait
[__link21]: https://docs.rs/sea_orm/1.1.19/sea_orm/?search=TransactionTrait::transaction
[__link22]: https://docs.rs/sea_orm/1.1.19/sea_orm/?search=DatabaseTransaction
[__link23]: https://docs.rs/sea-orm-ffi/0.1.1/sea_orm_ffi/?search=FfiConnection::begin
[__link24]: https://docs.rs/sea-orm-ffi/0.1.1/sea_orm_ffi/?search=FfiTransaction
[__link25]: https://docs.rs/sea_orm/1.1.19/sea_orm/?search=ConnectionTrait
[__link26]: https://docs.rs/sea_orm/1.1.19/sea_orm/?search=DatabaseTransaction
[__link27]: https://crates.io/crates/sea_orm/1.1.19
[__link28]: https://docs.rs/sea_orm/1.1.19/sea_orm/?search=Value
[__link29]: https://crates.io/crates/sea_orm/1.1.19
[__link3]: https://docs.rs/sea_orm/1.1.19/sea_orm/?search=ConnectionTrait
[__link30]: https://crates.io/crates/sea_orm/1.1.19
[__link31]: https://codeberg.org/proto-x/sea-orm-ffi/issues
[__link32]: https://codeberg.org/proto-x/sea-orm-ffi/pulls
[__link33]: https://crates.io/crates/refinery
[__link34]: https://docs.rs/sea-orm-ffi/0.1.1/sea_orm_ffi/?search=FfiConnection
[__link35]: https://docs.rs/refinery_core/0.9.0/refinery_core/?search=AsyncMigrate
[__link36]: https://docs.rs/refinery_core/0.9.0/refinery_core/?search=Runner::run_async
[__link37]: https://codeberg.org/proto-x/sea-orm-ffi/releases
[__link4]: https://docs.rs/sea_orm/1.1.19/sea_orm/?search=TransactionTrait
[__link5]: https://docs.rs/sea_orm/1.1.19/sea_orm/?search=ConnectionTrait
[__link6]: https://doc.rust-lang.org/stable/std/?search=future::Future
[__link7]: https://crates.io/crates/async_ffi/0.5.0
[__link8]: https://crates.io/crates/tokio
[__link9]: https://crates.io/crates/libloading