Rust in Everything
基于 sqlx 0.9.0

升级到 0.9

主线位置:不在主线上。本页对着 0.9.0 的 CHANGELOG,把 0.8.6 到 0.9.0 的每一条和 Postgres 有关的破坏性改动过一遍,看它落在查询主线、连接池主线还是编译期检查主线的哪一段,再看本站经 sea-orm 用到的那几段会不会被碰到。sea-orm 2 本身的迁移(实体宏、查询 API 的变化)留到 sea-orm 那一卷。
flowchart TB
  site["本站代码"] --> orm1["sea-orm 1.1.20"]
  orm1 --> b07["sea-query-binder 0.7"]
  orm1 --> s086["sqlx 0.8.6"]
  b07 --> s086
  site -.->|升级后| orm2["sea-orm 2.0.4"]
  orm2 --> qs["sea-query-sqlx 0.9"]
  orm2 --> s090["sqlx 0.9.0"]
  orm2 --> core["sqlx-core 0.9.0"]
  qs --> s090

什么时候才会升

本站不直接依赖 sqlx(见本站怎么用 sqlx)。sea-orm 1.1.20 的 Cargo.toml 写的是 sqlx = { version = "0.8.4", default-features = false, optional = true },按 Cargo 的规则就是 >=0.8.4, <0.9.0,sea-query-binder 0.7.0 也绑在 0.8 上;在 sea-orm 1.x 上,cargo update 永远拿不到 0.9。sqlx 0.9 只会跟着 sea-orm 2 一起来。sea-orm 仓库里 tag 2.0.4(2026-09-27)的 Cargo.toml:

rust-version  = "1.94.0"
version       = "2.0.4"
# ...
sea-query = { version = "~1.0.0", default-features = false, features = [
# ...
sea-query-sqlx = { version = "0.9", default-features = false, optional = true }
# ...
sqlx = { version = "0.9.0", default-features = false, optional = true }
sqlx-core = { version = "0.9.0", default-features = false, optional = true }

三处变化:sqlx 到 0.9.0;把 sea-query 的值绑定给 sqlx 的 crate 从 sea-query-binder 换成了 sea-query-sqlx 0.9(sea-query 仓库里的提交 513d6e6 就叫「rename sea-query-binder to sea-query-sqlx」);多了一个对 sqlx-core 的直接依赖,原因在下面 TransactionManager 那一条。rust-version 和 sqlx 0.9.0 一样是 1.94.0。

CHANGELOG 的 Breaking 一节,逐条

0.9.0 的 Breaking 一节除了 MSRV 还列了 23 个 PR,下表只留和 Postgres、和本卷三条主线有关的(只影响 MySQL、SQLite、Any 的 #3924、#3928、#3957、#3958、#4142、#4255,以及纯依赖升级 #3670、#4094 不列)。「落在谁身上」指升级到 sea-orm 2 以后谁需要改代码。

PR改了什么主线落在谁身上本站
MSRV1.94.0—编译环境Dockerfile 用 rust:1-trixie(最新稳定版),本机 1.99.0;不受影响
#3383sqlx.toml,Migrate trait 大改编译期、迁移用 migrate! / Migrator 的人不用 sqlx 迁移,不受影响
#3486慢获取日志字段 aquired_after_secs 改成 acquired_after_secs连接池解析日志的人日志里看得见,见下文
#3495PgAdvisoryLockGuard 去掉生命周期—用 advisory lock 的人不用
#3526Migrator::set_* 返回 &mut Self迁移用 Migrator 的人不用
#3541query! describe 时强制 generic plan编译期用宏的人没开 macros
#3613RawSql 的方法多了 DB 类型参数查询用 raw_sql 的人sea-orm 2.0.4 的源码里没有用 sqlx 的 raw_sql
#3674Cow 一律解成 Owned查询(解码)用 Cow 借数据的人sea-orm 的模型字段都是自有类型
#3723query*() 只收 SqlSafeStr查询sea-orm本站代码不调 sqlx::query,见下文
#3800PgConnectOptions::options() 自动转义连接手动转义过选项的人URL 里没有 options;sea-orm 2.0.4 新加的 statement_timeout 经它设置
#3821删掉 runtime+TLS 组合 feature;门面不再导出 TransactionManager—sea-orm见下文
#3949PgLTree::from 改成 From<Vec<PgLTreeLabel>>—用 ltree 的人不用
#3960Arguments 去掉生命周期参数查询sea-query 的绑定 crate见下文
#3993passfile 里的密码处理反斜杠转义连接用 .pgpass 的人密码在 DATABASE_URL 里,不读 passfile
#4008#[derive(sqlx::Type)] 给 newtype 自动生成 PgHasArrayType编译期(派生)手写过这个 impl 的人不用 sqlx 的派生
#4077offline 变成可选—关掉默认 feature 的人0.8.6 门面把 offline 写死在对 sqlx-core 的依赖里,0.9 起不再强制

大半条目要么在宏和迁移里,要么在本站根本不用的类型和功能上。真正碰到本站这条路径的只有三条,都在 sea-orm 和 sqlx 的接缝上。

#3723:SqlSafeStr

0.8.6 的 query_with 收 &'q str,0.9.0 的 query_with 收 impl SqlSafeStr(机制和报错见 Query、QueryAs 与 SqlSafeStr)。sea-orm 1.1.20 的 sqlx_query 写的是 sqlx::query_with(&stmt.sql, SqlxValues(values)),stmt.sql 是运行时生成的 String,在 0.9 上编译不过(sql-safe-str)。sea-orm 2.0.4 的同一个函数(src/driver/sqlx_postgres.rs):

pub(crate) fn sqlx_query(stmt: &Statement) -> sqlx::query::Query<'_, Postgres, SqlxValues> {
    let values = stmt
        .values
        .as_ref()
        .map_or(Values(Vec::new()), |values| values.clone());
    sqlx::query_with(sqlx::AssertSqlSafe(stmt.sql.as_str()), SqlxValues(values))
}

execute_unprepared 和 set_transaction_config 交给 sqlx 的 SQL 也都包上了 AssertSqlSafe。SQL 全由 sea-query 拼出来、值全走绑定参数,这正是 AssertSqlSafe 设想的「审过了」的场合;本站代码一次都没有直接调 sqlx::query,没有要改的地方。一个小代价:AssertSqlSafe<&str> 的 SqlSafeStr 实现上方的注释是「Note: copies the string.」,into_sql_str 把它拷成 Repr::Arced,所以每条查询比 0.8.6 多一次分配。

#3960:Arguments 去掉生命周期

0.8.6 的 IntoArguments<'q, DB> 和 Arguments<'q> 带着 'q,sea-query-binder 0.7.0 实现的是 impl sqlx::IntoArguments<'_, sqlx::postgres::Postgres> for SqlxValues。0.9 把这个生命周期删了(Database 与关联类型的冷知识),这个 impl 在 0.9 上写不出来。sea-query 仓库里的 sea-query-sqlx(0.9.1)写的是 impl sqlx::IntoArguments<sqlx::postgres::Postgres> for SqlxValues,没有生命周期参数。本站代码不实现也不调用 Arguments。

#3821:组合 feature 和 TransactionManager

本站工作区写的 sea-orm feature 是 runtime-tokio-rustls。在 sea-orm 1.1.20 里它展开成 sqlx 的组合 feature sqlx?/runtime-tokio-rustls,0.9 把这类组合 feature 删了(源码地图的 feature 一节)。sea-orm 2.0.4 保留了自己的 feature 名字,改了展开方式:

runtime-tokio = ["sqlx?/runtime-tokio"]
runtime-tokio-native-tls = ["sqlx?/tls-native-tls", "runtime-tokio"]
runtime-tokio-rustls = ["sqlx?/tls-rustls", "runtime-tokio"]

tls-rustls 在 0.9 里是 tls-rustls-ring 的别名,后者又指向 tls-rustls-ring-webpki,和 0.8.6 下本站实际用的 TLS 后端相同。sqlx-postgres、macros 这两个 sea-orm feature 在 2.0.4 里也都还在,本站 Cargo.toml 里的 feature 列表不用改名。

另一半是 TransactionManager。0.8.6 的门面 src/lib.rs 是 pub use sqlx_core::transaction::{Transaction, TransactionManager};,0.9.0 的同一处只剩 Transaction。sea-orm 的事务正是绕过 sqlx::Transaction、直接调 TransactionManager 的(1.1.20 的 src/database/transaction.rs 开头是 use sqlx::TransactionManager;),这一条正好落在它身上。

📝NOTE
冷知识:CHANGELOG 里点了 SeaORM 的名

这一条在 sqlx 的 CHANGELOG 里写成「Deleted re-export of unstable TransactionManager trait in sqlx.」,下面一行是「Not technically a breaking change because it's #[doc(hidden)], but it will break SeaORM if not proactively fixed.」PR #3821(「groundwork for 0.9.0-alpha.1」,首次发布于 0.9.0)的说明里是同样的话,那个链接指向 sea-orm 仓库里的一条讨论。删掉它的提交说明只有一行「chore: don't re-export unstable TransactionManager trait」。sea-orm 2.0.4 的应对是直接依赖 sqlx-core,在 src/database/transaction.rs 开头写 use sqlx_core::transaction::TransactionManager;,Cargo.toml 的 sqlx-dep feature 也从空列表变成了 ["sqlx", "sqlx-core"]。

不在 Breaking 里,但本站看得见的

有两处改动不需要改代码,升级后的线上行为会变。

错误文本多了 at line N。 #4118 让 PgDatabaseError 的 Display 在消息后面接上 at line {L},L 是服务端 C 源码的行号(错误里 0.8.6 和 0.9.0 的对照输出)。sea-orm 2.0.4 的 DbErr 仍然是 Execution Error: {0}、Query Error: {0} 这样把 sqlx 的 Display 原样接在后面,本站很多 server 函数又用 ServerFnError::new(e.to_string()) 把它交给前端。本站怎么用 sqlx 里主键冲突的那一行,现在是 Execution Error: error returned from database: duplicate key value violates unique constraint "post_pkey",升级后末尾会多出一个随 Postgres 版本变化的 at line N。本站代码里没有按数据库错误的文本或错误码做判断的地方(对 to_string().contains、23505、is_unique_violation 的 git grep 只找到两处断言支付模块自己错误类型的测试),影响只是展示和日志。

慢获取日志的字段名。 本站生产日志里那条 sqlx::pool::acquire 的 WARN,0.8.6 打的是 aquired_after_secs,0.9 是 acquired_after_secs(#3486)。按字段名做告警的要跟着改;本站没有。

还有几条看上去相关、其实碰不到本站:

  • #3980 改的是 sqlx_core::Transaction::begin(先置 open 再发 BEGIN),它带来的「事务里 begin_with 回滚外层事务」(#4445)也在那里。sea-orm 2.0.4 和 1.1.20 一样不经过 Transaction,直接调 <sqlx::Postgres as sqlx::Database>::TransactionManager::begin(c, None),两者都碰不到。反过来,「取消 begin 让连接带着事务回池」在 0.9.0 的 PgTransactionManager::begin 里还在,修复 #4394 合并在 0.9.0 之后;升到 sea-orm 2.0.4 加 sqlx 0.9.0,本站页上 cancel_begin.rs 复现的那个坑不会消失(cancelled-begin)。
  • statement_cache_capacity(0) 泄漏命名语句(#4328,0.9.0 里还在):本站不设语句缓存容量。
  • #4226 把自定义类型的元数据查询改成批量的简单查询:本站没有自定义类型(Postgres 类型)。
  • #3952 让 Pool::close().await 等所有连接真正关完:本站代码不调 close,进程退出时连接随之断开。
  • persistent(false) 用未命名语句(#3863)在 0.8.6 里就有了,sea-orm 也不用 persistent(false)。

sea-orm 2.0.4 的连接路径

只看和 sqlx 交接的那一段。src/driver/sqlx_common.rs 的 sqlx_pool_options 和 1.1.20 的顺序、条件都一样,connect_timeout 仍然写进 acquire_timeout,然后被 acquire_timeout 覆盖;差别是 idle_timeout 和 max_lifetime 现在存的是 Option<Option<Duration>>,可以显式设成 None 关掉。SqlxPostgresConnector::connect 照旧从 URL 解析 PgConnectOptions、按同样的条件设语句日志和慢查询日志,ConnectOptions::new 的默认值也还是 sqlx_logging_level: Info、sqlx_slow_statements_logging_level: Off、test_before_acquire: true。多出来的几个选项都是对 sqlx 已有能力的透传:application_name 交给 PgConnectOptions::application_name,statement_timeout 交给 options([("statement_timeout", ..)]),test_before_acquire_if_idle_for 和用户的 before_acquire 回调被组合进 sqlx 唯一的那个 before_acquire 槽位(设了阈值就把 test_before_acquire 关掉,只对空闲超过阈值的连接 ping,正是 PoolOptions 与默认值里文档举的那个用法),after_connect 也能设了。

本站 build_connect_options 用到的六个方法在 2.0.4 里都还在:max_connections(u32)、min_connections(u32)、connect_timeout(Duration)、acquire_timeout(Duration)、sqlx_logging_level(log::LevelFilter) 签名不变,idle_timeout 改成了 T: Into<Option<Duration>>,传 Duration 照样能编译。sea-orm-migration 2.0.4 的源码里也没有 advisory lock(git grep -i advisory 没有结果),本站怎么用 sqlx 里迁移不加锁的那一点升级后不变;它的事务包法有改动,属于 sea-orm 那一卷。

小结

  • 本站经 sea-orm 用 sqlx,sea-orm 1.x 锁在 0.8.x,sqlx 0.9 只会随 sea-orm 2 一起来;sea-orm 2.0.4 依赖 sqlx 0.9.0、sea-query 1.0、sea-query-sqlx 0.9,并直接依赖 sqlx-core,MSRV 同为 1.94。
  • 0.9.0 和 Postgres 有关的破坏性改动里,SqlSafeStr(#3723)、Arguments 的生命周期(#3960)、组合 feature 和 TransactionManager 的导出(#3821)落在 sea-orm 身上,2.0.4 已经吸收:AssertSqlSafe 包住 sea-query 的 SQL,换用 sea-query-sqlx,从 sqlx-core 拿 TransactionManager。其余条目在宏、迁移或本站不用的类型上。
  • 本站自己的代码在 sqlx 这一层没有要改的:没有直接的 sqlx 调用,Cargo.toml 的 sea-orm feature 名不变,build_connect_options 用到的方法都在。
  • 看得见的变化有两处:错误文本末尾多了服务端源码行号(#4118),慢获取日志的字段从 aquired_after_secs 改成 acquired_after_secs(#3486)。
  • 取消 begin 让连接带着事务回池的问题,sqlx 0.9.0 加 sea-orm 2.0.4 仍然存在;事务里 begin_with 的回归碰不到 sea-orm。

💬 关联讨论

发起讨论
加载中...