
postgres_lsp 安全规则详解avoidCreateTrigger 如何拦截阻塞写入的 CREATE TRIGGER【免费下载链接】postgres_lspA Language Server for Postgres项目地址: https://gitcode.com/GitHub_Trending/po/postgres_lsp导读avoidCreateTrigger是 postgres_lsp 内置 lint 规则集中safety安全分组下的一条静态分析规则用于在 SQL 代码中检测CREATE TRIGGER语句并发出告警。本篇文章以该规则的官方参考文档为骨架结合其 Rust 源码实现、配置注册表与快照测试完整讲解规则的诊断语义、触发条件、配置方式与典型实战场景帮助你在 CI、编辑器与数据库迁移检查中落地这条规则规避触发器带来的行级写锁阻塞与隐性性能风险。规则概览基本信息与适用场景根据 docs/reference/rules/avoid-create-trigger.md规则元信息如下诊断分类Diagnostic Categorylint/safety/avoidCreateTrigger版本Sincevnext规则来源Sources灵感源自开源项目pgfence的create-trigger检查项从源码crates/pgls_analyser/src/lint/safety/avoid_create_trigger.rs中可以进一步确认规则的完整声明pub AvoidCreateTrigger { version: next, name: avoidCreateTrigger, severity: Severity::Warning, recommended: false, sources: [RuleSource::Pgfence(create-trigger)], }由此可以得到几条实现事实规则默认严重级别为 Warning警告与crates/pgls_configuration/src/linter/rules.rs中severity()函数映射的avoidCreateTrigger Severity::Warning一致默认不随推荐集启用recommended: false需要用户在配置中显式开启该规则归属safety分组在crates/pgls_analyser/src/lint/safety.rs的declare_lint_group!中与banTruncate、banDropTable、requireConcurrentIndexCreation等 50 余条规则并列注册并在crates/pgls_analyser/src/registry.rs中按规则名完成动态查找因此它既能被 CLI 的check/dblint命令加载也能被 LSP 的编辑时诊断通道使用。适用场景数据库迁移脚本审查、Schema 变更的 CI 门禁、以及编辑器内对含触发器的 SQL 文件的实时告警。它面向的是一类触发器治理诉求团队希望在应用层或异步任务中实现等效逻辑而非在表上叠加数据库触发器。为什么禁用触发器SHARE ROW EXCLUSIVE 锁与隐性复杂度规则文档明确指出核心依据Creating a trigger acquires aSHARE ROW EXCLUSIVElock on the table.即CREATE TRIGGER会在目标表上获取SHARE ROW EXCLUSIVE锁规则的告警信息原文为Creating a trigger acquires a SHARE ROW EXCLUSIVE lock on the table.结合 Postgres 的锁语义可以理解其风险链条SHARE ROW EXCLUSIVE与自身及SHARE UPDATE EXCLUSIVE、SHARE、SHARE ROW EXCLUSIVE、EXCLUSIVE、ACCESS EXCLUSIVE等锁模式互相冲突因此CREATE TRIGGER在执行期间可能阻塞表的并发写入尤其是高频写入场景延长 DDL 的锁等待窗口触发器一旦创建每次写操作都会隐式触发函数调用为写路径引入额外开销与隐藏的副作用性能问题难以定位排查复杂。源码实现把这一语义直接写进了诊断消息。avoid_create_trigger.rs中通过markup!宏构造带强调样式的主消息并附带一条建议性 detailmarkup! { Creating a trigger acquires a EmphasisSHARE ROW EXCLUSIVE/Emphasis lock on the table. } .detail( None, Triggers add hidden complexity and can block concurrent writes. Consider using application-level logic instead., )也就是说规则给出的修复方向是优先考虑应用层逻辑把触发器的行为迁移到业务代码、消息队列或定期任务中从而消除锁竞争与隐性复杂度。这也是pgfence同款检查项的设计初衷。触发条件与实现原理如何识别 CREATE TRIGGER从实现上看AvoidCreateTrigger的检测逻辑极其直接规则没有可配置的 Optionstype Options ()其run方法只做一次 AST 节点类型匹配fn run(ctx: LinterRuleContextSelf) - VecLinterDiagnostic { let mut diagnostics vec![]; if let pgls_query::NodeEnum::CreateTrigStmt(_) ctx.stmt() { diagnostics.push(/* ... */); } diagnostics }这说明它基于 postgres_lsp 的解析层pgls_query对语句进行语法树分类当某条语句被解析为CreateTrigStmt即 PostgreSQL 语法中的CREATE TRIGGER语句时规则产生一条诊断匹配范围覆盖各种触发器变体只要语句顶层是CREATE TRIGGER即可命中包括BEFORE/AFTER/INSTEAD OF、FOR EACH ROW/FOR EACH STATEMENT、EXECUTE FUNCTION/EXECUTE PROCEDURE等写法其余语句如ALTER TABLE ... ADD CONSTRAINT、普通SELECT不会触发该规则。由于规则只针对顶层语句做匹配CREATE OR REPLACE TRIGGER、带IF NOT EXISTS的写法同样会落入CreateTrigStmt分支取决于具体语法解析结果可以理解为只要最终生成触发器 DDL 就告警的保守策略。运行效果CLI 诊断输出与快照验证规则文档给出了完整的告警渲染示例code-block.sql输入与终端输出。当执行 CLI 检查例如postgres-language-server check code-block.sql时命中后输出如下code-block.sql:1:1 lint/safety/avoidCreateTrigger ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Creating a trigger acquires a SHARE ROW EXCLUSIVE lock on the table. 1 │ create trigger my_trigger after insert on my_table for each row execute function my_func(); │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2 │ i Triggers add hidden complexity and can block concurrent writes. Consider using application-level logic instead.输出包含三要素主消息锁风险、源码位置与下划线标注精确到整条语句、建议信息改用应用层逻辑。该行为由仓库内的快照测试锁定。测试用例 crates/pgls_analyser/tests/specs/safety/avoidCreateTrigger/basic.sql 内容为-- expect_lint/safety/avoidCreateTrigger create trigger my_trigger after insert on my_table for each row execute function my_func();对应的 basic.sql.snap 快照断言了诊断分类与两级消息文本# Diagnostics lint/safety/avoidCreateTrigger ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ × Creating a trigger acquires a SHARE ROW EXCLUSIVE lock on the table. i Triggers add hidden complexity and can block concurrent writes. Consider using application-level logic instead.这一快照由crates/pgls_analyser/tests/rules_tests.rs驱动保证规则在任何后续改动中输出稳定。配置指南如何开启与调整等级规则默认不随推荐集启用需要显式配置。文档给出的标准配置如下{ linter: { rules: { safety: { avoidCreateTrigger: error } } } }配置值与语义配置值行为off完全关闭该规则默认状态warn命中时产生 Warning 级诊断不影响退出码门禁的强制要求error命中时产生 Error 级诊断check等命令在--error-on-warnings或 error 语义下会使其进入失败状态配置结构遵循 postgres_lsp 的分层模型linter.rules.safety.avoidCreateTrigger其中safety是规则分组名源码中Safety::GROUP_NAME safetyavoidCreateTrigger是规则名。在 crates/pgls_configuration/src/linter/rules.rs 中可以看到它被收录进GROUP_RULES常量数组并通过get_rule_configuration()与RuleConfiguration()无参数规则对接因此该规则没有任何可调参数配置仅限开/关与等级。实际落地方案CLI 配置将上述 JSON 写入项目根目录的postgres-language-server.jsonc仓库根目录已提供示例文件 postgres-language-server.jsonc随后执行postgres-language-server check ./migrations即可在迁移目录上生效LSP 编辑器集成在 IDE 设置中指向同一配置文件后编辑含CREATE TRIGGER的 SQL 文件时会实时看到诊断CI 门禁配合 docs/guides/continuous_integration.md 中的check命令将avoidCreateTrigger设为error可强制触发器 DDL 必须经过人工审批或改由应用层实现。补充说明何时保留触发器规则是治理工具而非绝对禁令。若团队确实需要触发器例如审计日志、级联维护、updated_at自动更新等低频场景可在配置中将该规则设为warn保留可见性或通过 suppressions 机制见 docs/guides/suppressions.md对特定文件/行豁免从而兼顾规则统一性与真实业务需求。与其他 safety 规则的配合avoidCreateTrigger与safety分组中多条规则构成互补的变更安全网avoid-enable-disable-trigger.md与ALTER TABLE ... ENABLE/DISABLE TRIGGER同属触发器治理系列二者联合可覆盖触发器生命周期的创建与启停两个阶段avoid-wide-lock-window.md针对宽锁窗口的同类告警强调锁竞争风险ban-drop-trigger.md与avoidCreateTrigger形成一禁一禁的对称策略防止删除触发器造成生产事故multiple-alter-table.md提示将多条 DDL 合并为单条ALTER TABLE减少锁获取次数与触发器锁风险同属锁治理话题。将上述规则统一置于safety分组并统一配置等级即可在迁移审查中建立锁 破坏性变更 触发器的多维防线。小结avoidCreateTrigger是一条简单但实用的安全 lint 规则基于 AST 的CreateTrigStmt匹配对每条CREATE TRIGGER语句发出 Warning 级诊断提示其SHARE ROW EXCLUSIVE锁可能阻塞并发写入并建议改用应用层逻辑。通过仓库源码crates/pgls_analyser/src/lint/safety/avoid_create_trigger.rs、配置注册crates/pgls_configuration/src/linter/rules.rs与快照测试crates/pgls_analyser/tests/specs/safety/avoidCreateTrigger/可以完整还原其实现与行为。在编辑器、CLI 与 CI 三个场景中只需一行配置即可启用是数据库迁移安全治理中成本最低、见效最快的检查项之一。【免费下载链接】postgres_lspA Language Server for Postgres项目地址: https://gitcode.com/GitHub_Trending/po/postgres_lsp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考