关于 sqlx-code-review
sqlx-code-review 是用于审查 Rust 项目中 sqlx 数据访问代码的技能,面向代码审查者。它结合查询、连接池、事务、类型映射和迁移清单,并要求在定级前核实文件与行号证据。
主要功能
- 01检查 Cargo.toml 与 sqlx 配置
- 02审查查询模式和绑定参数
- 03检查连接池与事务处理
- 04核对 Rust 与 SQL 类型映射
- 05按证据门槛标注问题严重性
适用场景
- 01审查 Rust 项目的 sqlx 查询
- 02检查连接池配置和事务回滚路径
- 03评估数据库迁移的可逆性与安全性
- 04按文件行号整理审查发现
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ existential-birds/ beagle --skill sqlx-code-review安装后在 Claude Code 中输入/sqlx-code-review确认已加载。
附带文件references/migrations.mdreferences/queries.md
详细介绍
sqlx 代码审查
审查工作流
- 检查 Cargo.toml — 记录 sqlx 功能(
runtime-tokio、tls-rustls/tls-native-tls、postgres/mysql/sqlite、uuid、chrono、json、migrate)和 Rust 版次(2024 版更改了 RPIT 生命周期捕获,并不再需要async-trait) - 检查查询模式 — 编译时检查(
query!、query_as!)与运行时检查(query、query_as) - 检查连接池配置 — 连接数限制、超时、空闲设置
- 检查迁移 — 文件命名、可逆性、数据迁移安全性
- 检查类型映射 — Rust 类型与 SQL 列类型是否匹配
关卡(先收集证据,再判定严重程度)
按顺序完成;在通过对应声明的关卡之前,不要判定为 Critical / Major。
- 范围 — 确认正在审查的 crate(
Cargo.toml路径)以及你打开的.rs文件(或目录)。通过条件: 至少注明一个实际检查过的具体路径。 - sqlx / 编译声明 — 在声称存在与
query!/query_as!、离线模式、sqlx.toml、DATABASE_URL或 Cargo 功能有关的问题之前,先打开相关的Cargo.toml,并在适用时打开sqlx.toml或已记录的环境配置。通过条件: 问题注明了行号,或说明这些文件不存在 / 不在审查范围内。 - 问题定位 — 每个报告的问题都必须按 输出格式 包含
[FILE:LINE]。通过条件: Critical 或 Major 问题均有行号引用。 - 协议 — 在完成第 1–3 项关卡之后、给出最终严重程度标签之前,加载并完成 review-verification-protocol 技能。通过条件: 每个保留的问题均满足协议步骤。
输出格式
按如下格式报告问题:
[FILE:LINE] ISSUE_TITLE
Severity: Critical | Major | Minor | Informational
Description of the issue and why it matters.
快速参考
| 问题类型 | 参考 |
|---|---|
| 查询宏、绑定参数、结果映射 | references/queries.md |
| 迁移、连接池配置、事务模式 | references/migrations.md |
审查清单
查询模式
- 尽可能使用编译时检查的查询(
query!、query_as!) - 配置
sqlx.toml或DATABASE_URL,以启用离线编译时检查 - 查询中不使用字符串插值(存在 SQL 注入风险)——使用绑定参数(
$1、$2) - 对公共 API,
query_as!映射到具名结构体,而非匿名记录 - 根据实际情况选择
.fetch_one()、.fetch_optional()、.fetch_all() - 对大型结果集使用
.fetch()(流式处理)
连接池
- 通过
Arc或框架状态共享PgPool(不要为每个请求创建) - 根据部署环境配置连接池大小(生产环境不要保留默认值)
- 设置连接获取超时
- 配置空闲连接清理
- 2024 版: 静态连接池单例初始化使用
std::sync::LazyLock(不要使用once_cell::sync::Lazy或lazy_static!)
事务
- 多语句操作使用
pool.begin() - 显式提交事务(不要依赖丢弃时隐式回滚)
- 事务中的错误在传播之前触发回滚
- 如有需要,嵌套事务使用保存点(
tx.begin())
类型映射
-
sqlx::Type派生与数据库列类型匹配 - Rust、serde 和 SQL 中的枚举表示保持一致
- 使用
Uuid、DateTime<Utc>、Decimal类型(不要用字符串表示结构化数据) - 可空列使用
Option<T> - JSONB 列使用
serde_json::Value - 枚举变体或结构体字段不要命名为
gen——它是 2024 版的保留关键字(使用r#gen并添加#[sqlx(rename = "gen")],或选择其他名称)
Edition 2024 兼容性
- 返回
-> impl Stream或-> impl Future的函数应考虑 RPIT 生命周期捕获变更(默认捕获所有作用域内的生命周期;使用+ use<'a>进行精确控制) - 自定义
FromRow或Typetrait 实现应在适用时使用 trait 中的原生async fn(无需#[async_trait],自 Rust 1.75 起稳定) - 仅在部分代码路径中使用的编译时查询字段,优先使用
#[expect(unused)]而非#[allow(unused)](可自行清理的 lint 抑制,自 Rust 1.81 起稳定) - 静态连接池初始化使用
std::sync::LazyLock(而非once_cell或lazy_static!)
迁移
- 迁移文件遵循命名约定(
YYYYMMDDHHMMSS_description.sql) - 破坏性迁移(DROP、ALTER DROP COLUMN)可逆,或有数据备份计划
- 不要在同一迁移中同时进行依赖数据的架构变更和数据变更
- 在应用启动时调用
sqlx::migrate!()
严重性校准
严重
- 在 SQL 查询中使用字符串插值(SQL 注入)
- 多语句写入缺少事务(出错时会发生部分写入)
- 每个请求都创建连接池(连接耗尽)
- 缺少绑定参数转义
主要
- 可使用编译时查询(
query!())验证正确性的场景,却使用运行时查询(query()) - 错误路径缺少事务回滚
- Rust 与数据库之间的枚举类型不匹配
- 对可能很大的表使用无界
.fetch_all() - 字段或变体命名为
gen,但未使用r#gen转义(edition 2024 编译失败)
次要
- 在生产环境中使用未经调优的连接池默认值
- 缺少
.fetch_optional()(使用.fetch_one(),然后通过处理错误来应对“未找到”) - 只需要特定列时,却使用范围过宽的
SELECT * - 查询列缺少索引(仅当查询模式明显缓慢时标记)
- Edition 2024:在
std::sync::LazyLock可用时仍使用once_cell::sync::Lazy或lazy_static! - 查询字段使用
#[allow(unused)]而非#[expect(unused)](优先采用可自行清理的 lint 抑制)
信息性
- 建议使用
query_as!进行类型安全的结果映射 - 建议在 Rust 校验之外添加数据库级约束
- 改进迁移组织方式
有效模式(不要标记)
- 对动态查询使用运行时
query()—— 编译时检查不适用于动态 SQL sqlx::FromRowderive —— 可作为query_as!的有效替代方案,用于可复用的行类型- 使用
TEXT列存储枚举 —— 配合sqlx::Typederive 有效,且比自定义 SQL 类型简单 .execute()忽略行数 —— 对幂等操作(upsert、删除)可以接受- 与其他语言共享数据库 —— 例如,由 Elixir 管理迁移,Rust 负责读取。这是有效的架构。
- 使用
r#gen并配合#[sqlx(rename = "gen")]—— 对数据库类型中的gen列,这是正确的 edition 2024 解决方案 - 在查询辅助函数的返回类型上使用
+ use<'a>—— 精确控制 RPIT 生命周期捕获(edition 2024) - 使用
std::sync::LazyLock初始化静态连接池 —— 替代once_cell/lazy_static(自 Rust 1.80 起稳定) - 在自定义
FromRow/Typetrait 实现中使用原生async fn—— 不再需要async-traitcrate(自 Rust 1.75 起稳定)
提交发现前
完成 Gates(严重性评定前的证据),然后加载并遵循 review-verification-protocol 技能,再报告任何问题。
评论
支持 Markdown