DEBUG.WIKI
丽萨主机;为跨境业务选好 IP;原生 / 住宅 IP · 按套餐选择;查看 IP →
PanstarCloud:部署应用,接入 AI,云服务器与 AI 网关
IPPeak:从目标地区访问网页,住宅代理、SEO 监测、广告验证

SQLAlchemy 关闭编译缓存后,from_statement 丢失 params 值

SQLAlchemy 2.1 的 ORM FromStatement 在无缓存键编译路径中可能丢失 params。正式 2.1.6 已修正;临时将值传给 Session.execute,保留参数绑定和原查询权限条件。

ORM 查询用 .from_statement(text(...)).params(n="a") 绑定了值,启用 query_cache_size=0 后却报 A value is required for bind parameter 'n'。默认缓存下正常,关闭缓存才失败时,应检查 SQLAlchemy 2.1 的 FromStatement 编译路径。

受影响实现会在缓存键中携带这些值;没有编译缓存键时,FromStatement 没有把自己的 .params() 正确注册给编译器。于是 stmt.compile().params 可能显示 {'n': None},最终执行也缺值。这不是数据库里不存在名为 n 的列。

正式 2.1.6 已修正无缓存键路径

SQLAlchemy 2.1.6 的正式发行说明包含这项修正。确认实际环境加载相应正式版本后,保留原来关闭缓存的配置重新核对,不能通过重新打开缓存就宣称无缓存分支已经恢复。

暂时不升级时,可以在执行入口传入参数,而不是依赖外层 FromStatement 的 .params():

from sqlalchemy import select, text

stmt = select(Task).from_statement(
    text("select * from task where name = :n")
)
items = session.execute(stmt, {"n": "a"}).scalars().all()

这里 Task 是已有映射,session 是应用当前事务中的 Session,表名和条件沿用该映射。实际查询的租户、权限及其他筛选必须保留。也可以在内部 text() 上按其公开接口绑定值,但应只选择一个清楚的参数归属,避免多处覆盖。

不要把参数插进 SQL 字符串

为了让打印出来的 SQL“带上值”,改成格式化字符串或拼接引号,会失去值绑定的保护,也无法解释这条编译回归。literal_binds 主要用于特定编译展示,不适合作为外部输入的执行修复。

该问题限定 ORM 的 FromStatement 与无缓存键路径。普通 select()、text() 或另一种 .params() 入口出现缺值时,仍需检查真实传参与占位符是否一致;不能只要看到“需要绑定参数”就断定是这个版本问题。

复核应比较默认缓存、关闭缓存和直接编译三种入口,分别使用两个不同参数值,确认查询确实按本次值返回结果。还要保留查不到记录的情况,避免一个固定样本恰好命中,掩盖值被固定或丢失的问题。