尧图精选

Postgres Language Server Safety 规则体系全解析:52 条安全 lint 规则索引与实战配置指南

🕒 发布时间:2026/9/18 4:26:58 📁 来源:尧图网络
Postgres Language Server Safety 规则体系全解析52 条安全 lint 规则索引与实战配置指南【免费下载链接】postgres_lspA Language Server for Postgres项目地址: https://gitcode.com/GitHub_Trending/po/postgres_lspPostgres Language Server本仓库postgres_lsp内置了一套面向 SQL 静态分析的 lint 规则系统其中 Safety安全组是覆盖最广、与线上数据库运维关系最密切的规则集合。rules.md 即该规则体系的官方索引列出了 Safety 组全部 52 条规则的名称、一句式功能描述与推荐属性。本文以该索引为骨架结合docs/reference/rules/下每条规则的详细文档、linting.md 配置指南以及crates/pgls_analyser的源码实现逐类讲解这些规则背后的数据库风险原理、配置方法、实现机制与测试验证方式帮助你理解哪些 SQL 会被拦、为什么被拦、如何放行。规则体系与推荐机制规则按功能分组group组织每一组聚焦一类问题域。当前文档体系包含两大索引rules.md静态 SQL 分析规则目前文档化的是Safety安全组共52 条database_rules.md基于 Splinter 驱动的数据库实例级 lint 规则Performance 组 7 条 Security 组 16 条需要连接真实数据库进行分析与静态规则互补。每个规则在索引表中用属性列标记是否属于recommended推荐规则图例约定为✅该规则属于推荐规则recommended rules默认配置下会启用lint 时产生诊断。在 linting.md 中可以看到推荐规则与配置的关系linter.rules.recommended默认开启时所有 ✅ 规则即被激活无需逐条手动声明。52 条 Safety 规则中有24 条带 ✅ 标记它们构成开箱即用的核心防护面。从文档元信息看这些规则大多标注Since: vnext说明它们随项目当前开发版本演进规则数量与细节以仓库内文档为准。Safety 规则全景按风险主题分组速查下面按风险主题将索引中的 52 条规则重新编排为五张速查表完整继承原索引的全部规则名、描述与推荐标记并给出指向 docs/reference/rules/ 的详情链接。你可以直接在 rules.md 中查看原始汇总。表重写与写阻塞风险ALTER TABLE 迁移类这类规则针对迁移中最常见的加字段、加约束导致整表重写或长时间持锁问题规则名描述属性addSerialColumn添加 SERIAL 类型或 GENERATED ALWAYS AS ... STORED 列会导致全表重写。✅addingFieldWithDefault添加带 DEFAULT 值的列可能导致表重写期间持有 ACCESS EXCLUSIVE 锁。✅addingForeignKeyConstraint添加外键约束需要对两张表做全表扫描并持有 SHARE ROW EXCLUSIVE 锁阻塞写入。✅addingNotNullField设置列 NOT NULL 会在扫描表期间阻塞读。✅addingPrimaryKeyConstraint添加主键约束会导致锁与表重写。✅addingRequiredField向已有表添加 NOT NULL 且无默认值的新列等于强制该列必填。avoidAddingExclusionConstraint添加排他约束会获取 ACCESS EXCLUSIVE 锁。✅changingColumnType修改列类型可能需要表重写并破坏现有客户端。constraintMissingNotValid不使用 NOT VALID 添加约束会阻塞所有读写。disallowUniqueConstraint禁止在未使用现有索引的情况下添加 UNIQUE 约束。multipleAlterTable对同一张表的多个 ALTER TABLE 语句应合并为一条。✅avoidWideLockWindow对多张表获取 ACCESS EXCLUSIVE 锁会扩大锁窗口。✅DDL 锁与并发操作CONCURRENTLY 相关围绕索引、物化视图、分区、触发器操作时的锁粒度与并发安全规则名描述属性avoidAttachingPartition挂载分区会对父表获取 ACCESS EXCLUSIVE 锁。✅avoidCreateTrigger创建触发器会对表获取 SHARE ROW EXCLUSIVE 锁。avoidEnableDisableTrigger启用或禁用触发器会获取 SHARE ROW EXCLUSIVE 锁。banDropTrigger删除触发器会对表获取 ACCESS EXCLUSIVE 锁。banConcurrentIndexCreationInTransaction事务块内不允许并发建索引。✅requireConcurrentDetachPartition不使用 CONCURRENTLY 分离分区会获取 ACCESS EXCLUSIVE 锁。✅requireConcurrentIndexCreation非并发创建索引会锁住表的写入。requireConcurrentIndexDeletion非并发删除索引会锁住表的读取。requireConcurrentRefreshMatview不使用 CONCURRENTLY 刷新物化视图会获取 ACCESS EXCLUSIVE 锁。✅requireConcurrentReindex不使用 CONCURRENTLY 执行 REINDEX 会对表获取 ACCESS EXCLUSIVE 锁。✅concurrentRefreshMatviewLockREFRESH MATERIALIZED VIEW CONCURRENTLY仍会获取 EXCLUSIVE 锁。runningStatementWhileHoldingAccessExclusive持有 ACCESS EXCLUSIVE 锁期间继续执行其他语句会阻塞该表的一切访问。✅lockTimeoutWarning未设置锁超时即获取危险锁可能导致无限期阻塞。✅requireIdleInTransactionTimeout危险锁语句前应执行SET idle_in_transaction_session_timeout。requireStatementTimeout危险锁语句前应执行SET statement_timeout。破坏性操作防护DROP / DELETE / TRUNCATE / VACUUM针对线上数据丢失与误操作的高危语句规则名描述属性banDeleteWithoutWhere无 WHERE 的 DELETE 会删除表中所有行。✅banUpdateWithoutWhere无 WHERE 的 UPDATE 会修改表中所有行。✅banTruncateTRUNCATE 会清空所有行并可能在生产环境造成数据丢失。✅banTruncateCascade使用 TRUNCATE 的 CASCADE 选项会级联清空所有外键引用表。banVacuumFullVACUUM FULL 会重写整张表并获取 ACCESS EXCLUSIVE 锁。✅banDropColumn删除列可能破坏现有客户端。✅banDropDatabase删除数据库可能破坏现有客户端以及一切其他东西。banDropNotNull删除 NOT NULL 约束可能破坏现有客户端。✅banDropSchema删除 schema 会移除其中所有对象并可能破坏现有客户端。✅banDropTable删除表可能破坏现有客户端。✅avoidAlterEnumAddValue在旧版 Postgres 中ALTER TYPE ... ADD VALUE不能在事务块内执行。类型与建表最佳实践偏好类规则引导使用更稳健、更符合现代 Postgres 实践的类型规则名描述属性banCharField不鼓励使用 CHAR(n) 或 CHARACTER(n) 类型。preferBigInt优先使用 BIGINT 而非较小的整数类型。preferBigintOverInt优先使用 BIGINT 而非 INT/INTEGER。preferBigintOverSmallint优先使用 BIGINT 而非 SMALLINT。preferIdentity优先使用 IDENTITY 列而非 serial 列。preferJsonb优先使用 JSONB 而非 JSON。preferTextField优先使用 TEXT 而非 VARCHAR(n)。preferTimestamptz优先使用 TIMESTAMPTZ 而非 TIMESTAMP。creatingEnum不推荐新应用创建 enum 类型。preferRobustStmts迁移中优先使用带保护guard的健壮语句。命名与事务相关规则名描述属性renamingColumn重命名列可能破坏现有查询与应用代码。renamingTable重命名表可能破坏现有查询与应用代码。transactionNesting检测可能导致意外行为的问题事务嵌套。代表性规则深度剖析索引表只有一句话描述真正的风险解释在 docs/reference/rules/ 各规则详情页中。以下选取四条覆盖不同类型风险的规则展开说明。addSerialColumnSERIAL 与生成列的全表重写详情见 add-serial-column.md。向已有表添加 serial / bigserial / smallserial 列或GENERATED ALWAYS AS ... STORED列时Postgres 必须在持有 ACCESS EXCLUSIVE 锁的情况下重写整张表期间阻塞该表的一切读写。SERIAL 本质上是序列 DEFAULT 的组合而生成列需要为所有既有行计算并存储值两者都必须逐行重写。无效示例ALTER TABLE prices ADD COLUMN id serial; ALTER TABLE prices ADD COLUMN id bigserial; ALTER TABLE prices ADD COLUMN total int GENERATED ALWAYS AS (price * quantity) STORED;对应的 lint 输出摘自文档格式与 CLI 终端输出一致code-block.sql:1:1 lint/safety/addSerialColumn ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Adding a column with type serial requires a table rewrite. 1 │ ALTER TABLE prices ADD COLUMN id serial; │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2 │ i SERIAL types require rewriting the entire table with an ACCESS EXCLUSIVE lock, blocking all reads and writes. i SERIAL types cannot be added to existing tables without a full table rewrite. Consider using a non-serial type with a sequence instead.规则给出的替代思路是改用非 serial 类型 显式序列从而避免全表重写。addingFieldWithDefault默认值在 Postgres 11 前后的差异详情见 adding-field-with-default.md。在 Postgres 11 之前的版本中给列添加 DEFAULT 值会导致全表重写并持有 ACCESS EXCLUSIVE 锁Postgres 11 对非易变non-volatile默认值做了优化不再重写。但以下场景依然会重写易变默认值如random()或自定义函数生成列GENERATED ALWAYS AS始终需要表重写非易变默认值在 Postgres 11 下才是安全的。因此规则的推荐做法是拆成两步执行-- 先加列无默认值 ALTER TABLE core_recipe ADD COLUMN foo integer; -- 再单独设置默认值 ALTER TABLE core_recipe ALTER COLUMN foo SET DEFAULT 10; -- 如有需要再回填数据并添加 NOT NULL 约束而下面这条会触发诊断ALTER TABLE core_recipe ADD COLUMN foo integer DEFAULT 10;banDeleteWithoutWhere高危语句的源码实现详情见 ban-delete-without-where.md其实现位于 ban_delete_without_where.rs。无 WHERE 的DELETE会清空整张表在迁移或应用上下文中几乎总是非故意的如果确实需要清空应显式使用TRUNCATE并知悉其影响或写WHERE true表明意图。无效写法delete from my_table;有效写法delete from my_table where expired_at now();该规则的源码实现非常直观展示了整个 linter 的通用范式declare_lint_rule! { pub BanDeleteWithoutWhere { version: next, name: banDeleteWithoutWhere, severity: Severity::Warning, recommended: true, sources: [RuleSource::Pgfence(delete-without-where)], } } impl LinterRule for BanDeleteWithoutWhere { type Options (); fn run(ctx: LinterRuleContextSelf) - VecLinterDiagnostic { let mut diagnostics vec![]; if let pgls_query::NodeEnum::DeleteStmt(stmt) ctx.stmt() stmt.where_clause.is_none() { diagnostics.push( LinterDiagnostic::new( rule_category!(), None, markup! { A EmphasisDELETE/Emphasis without a EmphasisWHERE/Emphasis clause will remove all rows from the table. }, ) .detail(None, Add a WHERE clause to limit which rows are deleted.), ); } diagnostics } }可以看到规则通过pgls_query::NodeEnum::DeleteStmt匹配 AST 中的 DELETE 语句检查where_clause是否为空为空则生成一条带高亮强调的诊断并附带修复建议。recommended: true使其默认启用RuleSource::Pgfence(...)声明了规则移植来源详见后文规则来源。runningStatementWhileHoldingAccessExclusive持锁窗口扩大详情见 running-statement-while-holding-access-exclusive.md。当事务通过ALTER TABLE等语句获取 ACCESS EXCLUSIVE 锁后同一事务内继续执行其他语句会拉长持锁时间从而阻塞该表的所有 SELECT / INSERT / UPDATE / DELETE即使是SELECT COUNT(*)也会显著延长锁时长。规则建议将 ALTER TABLE 独立成事务其他操作放到单独事务中执行-- 无效持 ACCESS EXCLUSIVE 锁期间继续执行查询 ALTER TABLE authors ADD COLUMN email TEXT; SELECT COUNT(*) FROM authors; -- 有效ALTER TABLE 单独执行其他查询放单独事务 ALTER TABLE authors ADD COLUMN email TEXT;规则配置指南通过配置文件开启 / 关闭规则在项目根目录的 postgres-language-server.jsonc 中配置。推荐规则默认开启如需整体关闭再选择性开启可先设recommended: false{ linter: { enabled: true, rules: { recommended: false } } }每条规则支持四个严重级别与关闭态error、warn、info、hint、off。按规则组与规则名逐条配置{ linter: { rules: { safety: { banDropColumn: error, banDropTable: warn, addingRequiredField: off } } } }各规则详情页末尾均附有可直接复制的配置片段例如 ban-delete-without-where.md 中的{ linter: { rules: { safety: { banDeleteWithoutWhere: error } } } }配置文件采用 JSONC带注释的 JSON格式完整结构示例见 configuration.md可用files.include/files.ignore控制处理的文件范围。通过 CLI 精确控制无需改动配置文件时可直接用 CLI 精确指定规则范围详见 linting.md 与 cli.md# 检查整个迁移目录 postgres-language-server check migrations/ # 只启用某条规则 postgres-language-server check migrations/ --only safety/banDropColumn # 跳过某条规则 postgres-language-server check migrations/ --skip safety/banDropTable在 SQL 内抑制单条诊断对有意为之的写法可用注释精确抑制格式为-- pgls-ignore 规则类别: 原因-- pgls-ignore lint/safety/banDropColumn: Intentionally dropping deprecated column ALTER TABLE users DROP COLUMN deprecated_field; -- pgls-ignore lint/safety/banDropTable: Cleanup during migration DROP TABLE temp_migration_table;抑制机制的完整说明见 suppressions.md解析实现位于 pgls_suppressions 的 parser.rs。规则系统的实现机制Safety 规则全部位于 crates/pgls_analyser/src/lint/safety/目录内 52 个规则文件与索引中的 52 条规则一一对应如add_serial_column.rs、ban_update_without_where.rs、require_concurrent_reindex.rs等通过 safety.rs 汇总注册。从 ban_delete_without_where.rs 的实现可以归纳出这套规则框架的关键构件declare_lint_rule!宏定义于 pgls_analyse 的 macros.rs声明规则元数据——版本、名称、默认严重级别、是否推荐、来源LinterRuletrait规则核心接口run()接收LinterRuleContext内含解析后的语句ctx.stmt()返回VecLinterDiagnosticLinterDiagnostic携带规则类别、高亮消息与detail修复建议的诊断对象AST 访问通过 pgls_query 的NodeEnum匹配语句节点如DeleteStmt并结合where_clause、列定义等子节点判断触发条件文档与源码同源规则详情页中的示例注释如sql,expect_diagnostic直接内嵌在源码的declare_lint_rule!注释中保证文档与实现不脱节。规则来源与移植背景索引中相当一部分规则移植自成熟的 Postgres 迁移安全工具这一点在 rule_sources.md 中有系统性记录主要来源包括Eugene如addSerialColumnE11、preferJsonbE3、runningStatementWhileHoldingAccessExclusiveE4、lockTimeoutWarningE9、multipleAlterTableW12、creatingEnumW13Squawk如addingFieldWithDefault、addingForeignKeyConstraint、addingNotNullField、banDropColumn、banDropTable、preferIdentity、preferTextField、renamingColumn、renamingTable、transactionNesting等约 27 条pgfence如avoidAddingExclusionConstraint、avoidAttachingPartition、banDeleteWithoutWhere、banTruncate、banVacuumFull、requireConcurrentReindex、requireConcurrentRefreshMatview、requireIdleInTransactionTimeout、requireStatementTimeout等约 18 条。各规则详情页的Sources一节标注了具体出处源码中则通过RuleSource::Eugene(...)/RuleSource::Squawk(...)/RuleSource::Pgfence(...)声明。移植意味着这些规则背后的数据库行为判断锁等级、表重写条件、版本差异经过了外部工具生态的验证同时又以本项目的 AST 与诊断框架重新实现。测试与验证每个规则的文档示例都有对应的自动化测试支撑。规则测试用例位于 crates/pgls_analyser/tests/specs/safety/包含 128 个.sql用例文件与 128 个.snap快照文件由 rules_tests.rs 驱动每个 SQL 用例通过 lint 后与快照比对输出确保规则行为、诊断消息与文档示例保持一致。源码注释中的sql,expect_diagnostic标记即表明该示例应产生诊断实现文档即测试用例的效果。总结Safety 组是 Postgres Language Server 静态 lint 能力最核心的部分52 条规则覆盖了表重写风险、DDL 锁与并发操作、破坏性语句防护、类型偏好与命名影响等迁移与线上运维的关键场景其中 24 条推荐规则开箱即用。通过postgres-language-server.jsonc的linter.rules.safety段、CLI 的--only/--skip参数以及-- pgls-ignore注释你可以在不牺牲安全性的前提下按团队规范精确控制检查粒度。建议对照 rules.md 与各规则详情页结合本项目的迁移文件如 checking_migrations.md 指南逐步启用规则把危险 DDL 拦截在进入数据库之前。【免费下载链接】postgres_lspA Language Server for Postgres项目地址: https://gitcode.com/GitHub_Trending/po/postgres_lsp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联 返回资讯列表 →