Skip to content

Commit e0171d4

Browse files
committed
Update sea-orm-sync
1 parent 898892b commit e0171d4

14 files changed

Lines changed: 340 additions & 99 deletions

File tree

sea-orm-sync/src/database/executor.rs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,16 @@ use crate::{Schema, SchemaBuilder};
77
use std::future::Future;
88
use std::pin::Pin;
99

10-
/// A wrapper that holds either a reference to a [`DatabaseConnection`] or [`DatabaseTransaction`].
10+
/// A wrapper that holds either a reference to a [`DatabaseConnection`] or [`DatabaseTransaction`],
11+
/// or an owned [`DatabaseTransaction`].
1112
#[derive(Debug)]
1213
pub enum DatabaseExecutor<'c> {
1314
/// A reference to a database connection
1415
Connection(&'c DatabaseConnection),
1516
/// A reference to a database transaction
1617
Transaction(&'c DatabaseTransaction),
18+
/// An owned database transaction (used by migration's `SchemaManager::begin()`)
19+
OwnedTransaction(DatabaseTransaction),
1720
}
1821

1922
impl<'c> From<&'c DatabaseConnection> for DatabaseExecutor<'c> {
@@ -33,34 +36,39 @@ impl ConnectionTrait for DatabaseExecutor<'_> {
3336
match self {
3437
DatabaseExecutor::Connection(conn) => conn.get_database_backend(),
3538
DatabaseExecutor::Transaction(trans) => trans.get_database_backend(),
39+
DatabaseExecutor::OwnedTransaction(trans) => trans.get_database_backend(),
3640
}
3741
}
3842

3943
fn execute_raw(&self, stmt: Statement) -> Result<ExecResult, DbErr> {
4044
match self {
4145
DatabaseExecutor::Connection(conn) => conn.execute_raw(stmt),
4246
DatabaseExecutor::Transaction(trans) => trans.execute_raw(stmt),
47+
DatabaseExecutor::OwnedTransaction(trans) => trans.execute_raw(stmt),
4348
}
4449
}
4550

4651
fn execute_unprepared(&self, sql: &str) -> Result<ExecResult, DbErr> {
4752
match self {
4853
DatabaseExecutor::Connection(conn) => conn.execute_unprepared(sql),
4954
DatabaseExecutor::Transaction(trans) => trans.execute_unprepared(sql),
55+
DatabaseExecutor::OwnedTransaction(trans) => trans.execute_unprepared(sql),
5056
}
5157
}
5258

5359
fn query_one_raw(&self, stmt: Statement) -> Result<Option<QueryResult>, DbErr> {
5460
match self {
5561
DatabaseExecutor::Connection(conn) => conn.query_one_raw(stmt),
5662
DatabaseExecutor::Transaction(trans) => trans.query_one_raw(stmt),
63+
DatabaseExecutor::OwnedTransaction(trans) => trans.query_one_raw(stmt),
5764
}
5865
}
5966

6067
fn query_all_raw(&self, stmt: Statement) -> Result<Vec<QueryResult>, DbErr> {
6168
match self {
6269
DatabaseExecutor::Connection(conn) => conn.query_all_raw(stmt),
6370
DatabaseExecutor::Transaction(trans) => trans.query_all_raw(stmt),
71+
DatabaseExecutor::OwnedTransaction(trans) => trans.query_all_raw(stmt),
6472
}
6573
}
6674
}
@@ -72,6 +80,7 @@ impl TransactionTrait for DatabaseExecutor<'_> {
7280
match self {
7381
DatabaseExecutor::Connection(conn) => conn.begin(),
7482
DatabaseExecutor::Transaction(trans) => trans.begin(),
83+
DatabaseExecutor::OwnedTransaction(trans) => trans.begin(),
7584
}
7685
}
7786

@@ -87,6 +96,9 @@ impl TransactionTrait for DatabaseExecutor<'_> {
8796
DatabaseExecutor::Transaction(trans) => {
8897
trans.begin_with_config(isolation_level, access_mode)
8998
}
99+
DatabaseExecutor::OwnedTransaction(trans) => {
100+
trans.begin_with_config(isolation_level, access_mode)
101+
}
90102
}
91103
}
92104

@@ -97,6 +109,7 @@ impl TransactionTrait for DatabaseExecutor<'_> {
97109
match self {
98110
DatabaseExecutor::Connection(conn) => conn.begin_with_options(options),
99111
DatabaseExecutor::Transaction(trans) => trans.begin_with_options(options),
112+
DatabaseExecutor::OwnedTransaction(trans) => trans.begin_with_options(options),
100113
}
101114
}
102115

@@ -108,6 +121,7 @@ impl TransactionTrait for DatabaseExecutor<'_> {
108121
match self {
109122
DatabaseExecutor::Connection(conn) => conn.transaction(callback),
110123
DatabaseExecutor::Transaction(trans) => trans.transaction(callback),
124+
DatabaseExecutor::OwnedTransaction(trans) => trans.transaction(callback),
111125
}
112126
}
113127

@@ -128,6 +142,9 @@ impl TransactionTrait for DatabaseExecutor<'_> {
128142
DatabaseExecutor::Transaction(trans) => {
129143
trans.transaction_with_config(callback, isolation_level, access_mode)
130144
}
145+
DatabaseExecutor::OwnedTransaction(trans) => {
146+
trans.transaction_with_config(callback, isolation_level, access_mode)
147+
}
131148
}
132149
}
133150
}
@@ -159,7 +176,21 @@ impl<'c> IntoDatabaseExecutor<'c> for &'c DatabaseTransaction {
159176
}
160177
}
161178

179+
impl IntoDatabaseExecutor<'static> for DatabaseTransaction {
180+
fn into_database_executor(self) -> DatabaseExecutor<'static> {
181+
DatabaseExecutor::OwnedTransaction(self)
182+
}
183+
}
184+
162185
impl DatabaseExecutor<'_> {
186+
/// Returns `true` if this executor is backed by a transaction (borrowed or owned).
187+
pub fn is_transaction(&self) -> bool {
188+
matches!(
189+
self,
190+
DatabaseExecutor::Transaction(_) | DatabaseExecutor::OwnedTransaction(_)
191+
)
192+
}
193+
163194
/// Creates a [`SchemaBuilder`] for this backend
164195
pub fn get_schema_builder(&self) -> SchemaBuilder {
165196
Schema::new(self.get_database_backend()).builder()

sea-orm-sync/src/database/mod.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ pub struct ConnectOptions {
8888
pub(crate) schema_search_path: Option<String>,
8989
/// Application name (PostgreSQL only)
9090
pub(crate) application_name: Option<String>,
91+
/// Statement timeout (PostgreSQL only)
92+
pub(crate) statement_timeout: Option<Duration>,
9193
pub(crate) test_before_acquire: bool,
9294
/// Only establish connections to the DB as needed. If set to `true`, the db connection will
9395
/// be created using SQLx's [connect_lazy](https://docs.rs/sqlx/latest/sqlx/struct.Pool.html#method.connect_lazy)
@@ -212,6 +214,7 @@ impl ConnectOptions {
212214
sqlcipher_key: None,
213215
schema_search_path: None,
214216
application_name: None,
217+
statement_timeout: None,
215218
test_before_acquire: true,
216219
connect_lazy: false,
217220
after_connect: None,
@@ -371,6 +374,23 @@ impl ConnectOptions {
371374
self
372375
}
373376

377+
/// Set the statement timeout (PostgreSQL only).
378+
///
379+
/// This sets the PostgreSQL `statement_timeout` parameter via the connection options,
380+
/// causing the server to abort any statement that exceeds the specified duration.
381+
/// The timeout is applied at connection time and does not require an extra roundtrip.
382+
///
383+
/// Has no effect on MySQL or SQLite connections.
384+
pub fn statement_timeout(&mut self, value: Duration) -> &mut Self {
385+
self.statement_timeout = Some(value);
386+
self
387+
}
388+
389+
/// Get the statement timeout, if set
390+
pub fn get_statement_timeout(&self) -> Option<Duration> {
391+
self.statement_timeout
392+
}
393+
374394
/// If true, the connection will be pinged upon acquiring from the pool (default true).
375395
pub fn test_before_acquire(&mut self, value: bool) -> &mut Self {
376396
self.test_before_acquire = value;

sea-orm-sync/src/database/sea_schema_shim.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ impl sea_schema::Connection for crate::DatabaseExecutor<'_> {
4242
crate::DatabaseExecutor::Transaction(txn) => {
4343
<DatabaseTransaction as sea_schema::Connection>::query_all(txn, select)
4444
}
45+
crate::DatabaseExecutor::OwnedTransaction(txn) => {
46+
<DatabaseTransaction as sea_schema::Connection>::query_all(txn, select)
47+
}
4548
}
4649
}
4750

@@ -53,6 +56,9 @@ impl sea_schema::Connection for crate::DatabaseExecutor<'_> {
5356
crate::DatabaseExecutor::Transaction(txn) => {
5457
<DatabaseTransaction as sea_schema::Connection>::query_all_raw(txn, sql)
5558
}
59+
crate::DatabaseExecutor::OwnedTransaction(txn) => {
60+
<DatabaseTransaction as sea_schema::Connection>::query_all_raw(txn, sql)
61+
}
5662
}
5763
}
5864
}

sea-orm-sync/src/database/tracing_spans.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ macro_rules! db_span {
120120
let op = $crate::database::tracing_spans::DbOperation::from_sql(sql);
121121
::tracing::info_span!(
122122
$name,
123+
otel.kind = "client",
123124
db.system = $crate::database::tracing_spans::db_system_name($backend),
124125
db.operation = %op,
125126
db.statement = ::tracing::field::Empty,

sea-orm-sync/src/driver/rusqlite.rs

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use tracing::{debug, instrument, warn};
77

88
pub use OwnedRow as RusqliteRow;
99
use rusqlite::{
10-
CachedStatement, Row,
10+
CachedStatement, OpenFlags, Row,
1111
types::{FromSql, FromSqlError, Value},
1212
};
1313
pub use rusqlite::{
@@ -199,12 +199,52 @@ impl RusqliteConnector {
199199
// TODO handle disable_statement_logging
200200
let after_conn = options.after_connect;
201201

202-
let conn = RusqliteConnection::open(
203-
options
204-
.url
205-
.trim_start_matches("sqlite://")
206-
.trim_start_matches("sqlite:"),
207-
)
202+
let raw = options
203+
.url
204+
.trim_start_matches("sqlite://")
205+
.trim_start_matches("sqlite:");
206+
207+
let (path, mode) = match raw.find('?') {
208+
Some(q) => {
209+
let query = &raw[q + 1..];
210+
let mut mode = None;
211+
for kv in query.split('&') {
212+
if let Some(val) = kv.strip_prefix("mode=") {
213+
mode = Some(val);
214+
} else if !kv.is_empty() {
215+
return Err(DbErr::Conn(RuntimeErr::Internal(format!(
216+
"unsupported SQLite connection parameter: {kv}"
217+
))));
218+
}
219+
}
220+
(&raw[..q], mode)
221+
}
222+
None => (raw, None),
223+
};
224+
225+
let conn = match mode {
226+
None | Some("rwc") => RusqliteConnection::open(path),
227+
Some("ro") => RusqliteConnection::open_with_flags(
228+
path,
229+
OpenFlags::SQLITE_OPEN_READ_ONLY | OpenFlags::SQLITE_OPEN_NO_MUTEX,
230+
),
231+
Some("rw") => RusqliteConnection::open_with_flags(
232+
path,
233+
OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_NO_MUTEX,
234+
),
235+
Some("memory") => RusqliteConnection::open_with_flags(
236+
path,
237+
OpenFlags::SQLITE_OPEN_MEMORY
238+
| OpenFlags::SQLITE_OPEN_READ_WRITE
239+
| OpenFlags::SQLITE_OPEN_CREATE
240+
| OpenFlags::SQLITE_OPEN_NO_MUTEX,
241+
),
242+
Some(other) => {
243+
return Err(DbErr::Conn(RuntimeErr::Internal(format!(
244+
"unknown SQLite mode: {other}"
245+
))));
246+
}
247+
}
208248
.map_err(conn_err)?;
209249

210250
let conn = RusqliteSharedConnection {

sea-orm-sync/src/driver/sqlx_postgres.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ impl SqlxPostgresConnector {
8181
sqlx_opts = sqlx_opts.application_name(application_name);
8282
}
8383

84+
if let Some(timeout) = options.statement_timeout {
85+
sqlx_opts = sqlx_opts.options([("statement_timeout", timeout.as_millis().to_string())]);
86+
}
87+
8488
if let Some(f) = &options.pg_opts_fn {
8589
sqlx_opts = f(sqlx_opts);
8690
}

sea-orm-sync/src/entity/active_model_ex_.rs

Lines changed: 0 additions & 90 deletions
This file was deleted.

sea-orm-sync/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,8 @@
600600
//! + [A walk-through of SeaORM 2.0](https://www.sea-ql.org/blog/2025-12-05-sea-orm-2.0/)
601601
//! + [How we made SeaORM synchronous](https://www.sea-ql.org/blog/2025-12-12-sea-orm-2.0/)
602602
//! + [SeaORM 2.0 Migration Guide](https://www.sea-ql.org/blog/2026-01-12-sea-orm-2.0/)
603+
//! + [SeaORM now supports Arrow & Parquet](https://www.sea-ql.org/blog/2026-02-22-sea-orm-arrow/)
604+
//! + [SeaORM 2.0 with SQL Server Support](https://www.sea-ql.org/blog/2026-02-25-sea-orm-x/)
603605
//!
604606
//! If you make extensive use of SeaQuery, we recommend checking out our blog post on SeaQuery 1.0 release:
605607
//!

sea-orm-sync/src/query/select.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::{
44
};
55
use core::fmt::Debug;
66
use core::marker::PhantomData;
7-
use sea_query::{IntoColumnRef, SelectStatement, SimpleExpr};
7+
use sea_query::{FunctionCall, IntoColumnRef, SelectStatement, SimpleExpr};
88

99
/// Defines a structure to perform select operations
1010
#[derive(Clone, Debug)]
@@ -210,6 +210,12 @@ impl IntoSimpleExpr for SimpleExpr {
210210
}
211211
}
212212

213+
impl IntoSimpleExpr for FunctionCall {
214+
fn into_simple_expr(self) -> SimpleExpr {
215+
SimpleExpr::FunctionCall(self)
216+
}
217+
}
218+
213219
impl<E> Select<E>
214220
where
215221
E: EntityTrait,

0 commit comments

Comments
 (0)