postgres_lsp multipleAlterTable 规则详解:合并同一张表的 ALTER TABLE 语句以减少表扫描与锁占用
【免费下载链接】postgres_lspA Language Server for Postgres项目地址: https://gitcode.com/GitHub_Trending/po/postgres_lsp
multipleAlterTable是 postgres_lsp(pgls_analyser)lint/safety诊断分类下的推荐规则,用于检测"对同一张表连续执行多条 ALTER TABLE 语句"并建议将其合并为单条逗号分隔的语句。本文将从规则语义、触发原理、源码实现、测试用例与配置方式五个层面,讲清这条规则为何存在、如何判定以及如何接入你的迁移脚本和 CI 流程。
规则概览:这是什么规则
| 属性 | 值 |
|---|---|
| 规则名 | multipleAlterTable |
| 诊断分类 | lint/safety/multipleAlterTable |
| 严重级别 | Warning(警告) |
| 是否推荐 | 是(recommended,开箱即产生诊断) |
| 引入版本 | vnext |
| 灵感来源 | eugene 的 W12 提示(agentic/port_eugene_rules.md 中记录的移植来源之一) |
该规则面向schema 变更脚本(迁移文件)场景:当你在一个文件中连续对同一张表执行多次ALTER TABLE时,它会给出诊断,提示将这些操作合并为一条语句。
为什么需要合并:表扫描、重写与锁
规则的 Description 给出了明确依据(见 docs/reference/rules/multiple-alter-table.md):
- 对同一张表执行多条
ALTER TABLE,Postgres 需要多次扫描、甚至多次重写表; - 每条
ALTER TABLE都要获取锁并执行表级操作,在大表上代价尤为昂贵; - 将多个操作合并进一条以逗号分隔 action 的语句,Postgres 只需扫描并修改表一次,从而提升性能、缩短持锁时间。
这一点也直接体现在规则源码的 doc 注释中(crates/pgls_analyser/src/lint/safety/multiple_alter_table.rs),并被完整继承到诊断信息的 detail 与 note 中。
触发示例:无效与有效写法
无效(会触发诊断)
ALTER TABLE authors ALTER COLUMN name SET NOT NULL; ALTER TABLE authors ALTER COLUMN email SET NOT NULL;这里连续两条语句都作用于authors表,每条都会触发一次表的锁获取与(潜在)重写。
有效(推荐写法)
ALTER TABLE authors ALTER COLUMN name SET NOT NULL, ALTER COLUMN email SET NOT NULL;将所有 action 合并为单条语句、以逗号分隔,Postgres 只需扫描并修改表一次。
触发诊断时的输出形态
从快照测试(crates/pgls_analyser/tests/specs/safety/multipleAlterTable/basic.sql.snap)可以看到实际诊断内容:
× Multiple ALTER TABLE statements found for table public.authors. i Multiple ALTER TABLE statements on the same table require scanning and potentially rewriting the table multiple times. i Combine the ALTER TABLE statements into a single statement with comma-separated actions to scan the table only once.诊断标题会明确指出"表public.authors上存在多条 ALTER TABLE 语句",并附上原因说明与修复建议。
源码级原理:规则是如何判定的
规则实现在 crates/pgls_analyser/src/lint/safety/multiple_alter_table.rs,核心逻辑如下:
- 识别当前语句:通过
pgls_query::NodeEnum::AlterTableStmt判断当前语句是否为ALTER TABLE,并取出relation.schemaname与relation.relname; - 归一化 schema:将空 schema 名归一化为
"public"——这意味着ALTER TABLE authors ...与ALTER TABLE public.authors ...会被视为同一张表; - 回溯历史语句:遍历
file_context().previous_stmts()(当前语句之前的全部语句),逐一比对是否为AlterTableStmt且relname与归一化后的 schema 均相同; - 产生诊断:一旦发现"前面的语句已对同一张表执行过 ALTER TABLE",即产生一条 Warning 级别的
LinterDiagnostic,包含标题、detail 与修复 note。
值得注意的边界行为:
- 该规则只回溯同文件内之前的语句,跨文件(跨迁移)的 ALTER 不会被合并判定;
- 判定仅针对同一张表(schema + 表名完全一致),不同表的交替 ALTER 不触发;
- 规则采用"遇到即报"策略,对连续多条同表 ALTER 会报告多次(每条后续语句各报一次),最终仍建议合并为单条语句。
规则通过declare_lint_rule!宏声明(severity 为 Warning、recommended 为 true、source 指向RuleSource::Eugene("W12")),并注册进safety规则组(见 crates/pgls_analyser/src/lint/safety.rs)。
测试用例:判定行为的四类验证
仓库在 crates/pgls_analyser/tests/specs/safety/multipleAlterTable/ 下提供了 4 组.sql+.snap快照测试,覆盖了规则的主要判定分支:
| 测试文件 | 场景 | 判定结果 |
|---|---|---|
| basic.sql | 同一张表连续两条 ALTER(默认 schema) | 触发,报告public.authors |
| with_schema.sql | 显式public.users连续两条 ALTER | 触发,报告public.users |
| three_alters.sql | CREATE TABLE 之后连续两条同表 ALTER | 触发,报告public.products(CREATE 不参与判定) |
| mixed_schema_notation.sql | 隐含 schema 与显式public.混用 | 触发——印证了"空 schema 归一化为 public"的匹配逻辑 |
其中mixed_schema_notation用例直接验证了源码中"将空 schema 视为 public"这一归一化设计,说明即使写法上带不带public.前缀,都会被识别为同一张表。
如何配置:接入配置与关闭规则
规则属于safety分组,可通过项目配置文件(仓库根目录的 postgres-language-server.jsonc 即为配置入口)启用或调整:
{ "linter": { "rules": { "safety": { "multipleAlterTable": "error" } } } }- 该规则默认推荐开启,不开配置也会产生 Warning 诊断;
- 将值设为
"error"可把警告提升为错误级别(例如在 CI 中要求必须修复); - 若确实需要保留逐条 ALTER 的写法,可设为
"off"关闭,或使用项目的 suppression 机制在指定位置豁免(参考 docs/guides/suppressions.md)。
配置解析与规则选择的底层实现可进一步参考 crates/pgls_analyser/src/options.rs 与 crates/pgls_analyser/src/registry.rs,这两个文件负责把配置项映射到具体规则的开启状态与严重级别。
与其他 safety 规则的协同
multipleAlterTable只是safety分组下 50 余条规则之一,同一分组内还有adding_not_null_field、adding_foreign_key_constraint、adding_primary_key_constraint、avoid_wide_lock_window、require_concurrent_index_creation等(完整清单见 crates/pgls_analyser/src/lint/safety.rs)。它们共同关注迁移与 DDL 操作对生产数据库的锁、重写与可用性影响:
- 迁移文件中常出现"连续加 NOT NULL 列、加外键、加约束"的组合,正是
multipleAlterTable的典型打击对象; - 与之互补,
require_concurrent_index_creation关注索引创建是否使用CONCURRENTLY,avoid_wide_lock_window关注长时间持锁的事务——合并 ALTER 恰好也是缩短持锁窗口的有效手段之一。
因此在实际迁移项目中,通常建议把该规则与同组规则一并开启,让 lint 在提交前就拦截高风险的 DDL 写法。规则的灵感来源 eugene/W12 也记录在 agentic/port_eugene_rules.md,可作进一步对照参考。
【免费下载链接】postgres_lspA Language Server for Postgres项目地址: https://gitcode.com/GitHub_Trending/po/postgres_lsp
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考