Postgres Language Server 数据库 Linting 实战指南:实时 Schema 性能与安全巡检
2026/9/18 8:16:02 网站建设 项目流程

Postgres Language Server 数据库 Linting 实战指南:实时 Schema 性能与安全巡检

【免费下载链接】postgres_lspA Language Server for Postgres项目地址: https://gitcode.com/GitHub_Trending/po/postgres_lsp

Postgres Language Server(postgres_lsp)的数据库 Linting 能力(Database Linting)直接连接你的在线 Postgres 数据库,对实际运行中的 Schema 状态进行体检,检测性能问题、安全漏洞与配置缺陷。本文以 docs/features/database_linting.md 为核心,结合仓库源码(pgls_splinterpgls_configurationpgls_cli)讲解其规则体系、配置方法、对象忽略策略、CLI 用法与数据库连接方式,读完即可在本地或 CI 中落地一套实时的数据库巡检方案。

与文件级 Linting 的区别:面向"活"的数据库

文件级 Linter 检查的是 SQL 迁移文件(如check命令对migration目录的静态分析),而数据库 Linter 的核心差异在于分析对象不同

维度文件级 Linter(File-based)数据库 Linter(Database)
分析对象SQL 迁移文件/代码文件在线数据库的实际 Schema 状态
数据来源文本解析 + AST连接数据库执行 SQL 查询
适用场景提交前、CI 中的迁移检查巡检线上库的真实健康状况
检测能力语法、风格、迁移风险主键缺失、索引冗余、RLS 未启用等运行态问题

两种模式互补:文件级 Linter 前置拦截迁移脚本问题,数据库 Linter 后置发现运行时 Schema 隐患。两者的规则实现完全不同——数据库规则由 Splinter 驱动(见 docs/features/database_linting.md 中的说明),每条规则的本质是一条针对系统目录表的 SQL 查询。

规则体系:性能与安全两大分组

数据库 Linting 的全部规则清单记录在 docs/reference/database_rules.md,共 23 条规则,划分为performancesecurity两个组。规则名采用 camelCase(如noPrimaryKey),分组名与规则名组合即诊断代码(splinter/performance/noPrimaryKey形式),源码中通过 crates/pgls_configuration/src/splinter/rules.rs 的Rules::get_severity_from_code解析诊断代码并反查配置的严重级别。

性能组(Performance,7 条)

规则名检测内容默认严重级别
authRlsInitplanRLS 策略中的current_setting()auth.<function>()调用是否被逐行重复求值Warning
duplicateIndex是否存在两个或以上完全相同的索引Warning
multiplePermissivePolicies同一张表、同一roleaction下是否存在多条 permissive RLS 策略(每条策略都会在每次相关查询中执行)Warning
noPrimaryKey表是否缺少主键(无主键的表在数据量大时交互效率低)Information
tableBloat表是否存在过多膨胀,是否需要VACUUM FULLCLUSTER维护Information
unindexedForeignKeys外键约束是否缺少覆盖索引Information
unusedIndex索引是否从未被使用,是否为删除候选Information

安全组(Security,16 条)

规则名检测内容默认严重级别
authUsersExposedauth.users是否通过暴露给 PostgREST 的 schema 中的视图/物化视图暴露给anonauthenticated角色Error
extensionInPublic是否在publicschema 中安装扩展Warning
extensionVersionsOutdated扩展是否未使用默认(推荐)版本Warning
fkeyToAuthUnique是否存在指向 auth schema 中唯一约束的用户自定义外键Error
foreignTableInApi是否有可通过 API 访问的外表(外表不遵守 RLS)Warning
functionSearchPathMutable函数是否未设置search_path参数Warning
insecureQueueExposedInApi是否有不安全的 Queue 暴露在 Data API 上Error
materializedViewInApi是否有物化视图暴露在 Data API 上Warning
policyExistsRlsDisabled是否创建了 RLS 策略但底层表未启用 RLSError
rlsDisabledInPublic暴露给 PostgREST 的 schema 中的表是否未启用 RLSError
rlsEnabledNoPolicy表已启用 RLS 但未创建任何策略Information
rlsPolicyAlwaysTrueRLS 策略是否使用USING (true)/WITH CHECK (true)等过度宽松表达式(SELECT 的USING (true)被有意排除,常被用于公开读访问)Warning
rlsReferencesUserMetadataRLS 策略中是否不安全地引用了 Supabase Auth 的user_metadataError
securityDefinerView是否存在以SECURITY DEFINER定义的视图(会以视图创建者而非查询用户的权限执行)Error
sensitiveColumnsExposed通过 API 暴露的表是否含 PII、凭据、财务等敏感列且无 RLS 保护Error
unsupportedRegTypes是否在pg_catalog之外使用不受支持的reg*类型列(会阻碍pg_upgrade升级)Warning

上表默认严重级别来自 crates/pgls_configuration/src/splinter/rules.rs 中Performance::severitySecurity::severity的实现,是未显式配置时的兜底值,可在配置文件中覆盖。

工作原理:一条规则 = 一段 SQL 查询

数据库规则与文件级规则最大的不同在于其执行模型。从 crates/pgls_splinter/src/rule.rs 的SplinterRuletrait 可以看出:

  • 规则逻辑以 SQL 文件形式存在,而非 Rust AST 逻辑;
  • 每条规则声明SQL_FILE_PATH(SQL 查询文件路径)、DESCRIPTIONREMEDIATIONREQUIRES_SUPABASE(是否要求 Supabase 角色)。

noPrimaryKey为例(crates/pgls_splinter/src/rules/performance/no_primary_key.rs),其规则体是查询pg_catalog.pg_class/pg_namespace/pg_index/pg_depend的 SQL,判断relkind = 'r'(普通表)且不存在indisprimary索引,并排除大量系统 schema 与扩展自有对象(pg_catalogauthextensionsstorage等),最后以jsonb_build_object输出schemanametype元数据。

在 crates/pgls_splinter/src/lib.rs 的run_splinter中可以看到完整的执行流程:

  1. 规则收集:通过RegistryVisitor模式按过滤器收集启用的规则名;
  2. Supabase 角色探测:检查 schema cache 中是否同时存在anonauthenticatedservice_role三个角色,不存在则跳过所有REQUIRES_SUPABASE = true的规则(这正是"自动跳过 Supabase 专属规则"的底层实现);
  3. SQL 合并执行:将各规则内嵌的 SQL(编译期经include_str!嵌入)以UNION ALL合并为一条大查询,外层再包一层ORDER BY "cache_key!"保证结果确定性;
  4. 事务与安全:在事务内执行set local search_path = ''后再运行查询,避免规则 SQL 受用户search_path影响;
  5. 忽略过滤:若配置了全局或按规则的 ignore 模式,则对每个诊断的schema.name标识符执行 glob 匹配并剔除命中的对象。

配置数据库 Linting

所有行为都在项目根目录的postgres-language-server.jsonc中配置。基础配置如下(来自 docs/features/database_linting.md):

{ "splinter": { // Enable/disable the database linter entirely "enabled": true, "rules": { // Configure rule groups "performance": { // Individual rule configuration "noPrimaryKey": "warn", "unusedIndex": "info" }, "security": { "rlsDisabledInPublic": "error", "authUsersExposed": "error" } } } }

配置项语义

从 crates/pgls_configuration/src/splinter/mod.rs 的SplinterConfiguration定义可以确认:

  • enabled:布尔值,false时整个数据库 Linter 不会执行,默认为true
  • ignore:跨所有规则的全局忽略 glob 列表;
  • rules:规则配置对象,包含recommendedall两个开关与performancesecurity两个分组。

从 crates/pgls_configuration/src/splinter/rules.rs 的Rules结构可以看到更细的语义:

  • rules.recommended:启用推荐的规则集,默认true(未显式配置时通过set_recommended隐式置真);
  • rules.all:启用全部规则(nursery 类规则除外);
  • 分组内也可单独设置performance.recommended/performance.all,且显式列出的单条规则会追加到启用集合,用"off"显式关闭的规则会从启用集合中剔除(as_enabled_rulesenabled_rules.difference(&disabled_rules)的集合运算逻辑);
  • 规则的默认严重级别见上一节的源码severity()函数,支持覆盖为"off"/"info"/"warn"/"error"

例如,若要关闭推荐集合并只保留两条自定义规则:

{ "splinter": { "rules": { "recommended": false, "performance": { "noPrimaryKey": "warn" }, "security": { "policyExistsRlsDisabled": "error" } } } }

忽略数据库对象:全局与按规则两级过滤

数据库 Linter 支持用 Unix 风格 glob 忽略特定数据库对象,格式为schema.object_name,其中*匹配任意字符序列(?匹配单个字符,见 crates/pgls_configuration/src/splinter/options.rs 的SplinterRuleOptions文档注释)。

全局忽略(所有规则生效)

适合排除整类对象,例如审计日志 schema 或临时表:

{ "splinter": { "ignore": [ "audit.*", "temp_*" ], "rules": { // ... } } }

按规则忽略(仅对指定规则生效)

将规则配置从简写形式改为对象形式,通过level+options.ignore表达:

{ "splinter": { "rules": { "performance": { "noPrimaryKey": { "level": "warn", "options": { "ignore": [ "public.temp_*", "staging.*" ] } } } } } }

模式示例

PatternMatches
public.my_tablepublic schema 中的特定表
audit.*audit schema 中的所有对象
*.temp_*任意 schema 中以 temp_ 为前缀的对象
public.log_*public schema 中以 log_ 开头的表

底层实现

源码中 glob 匹配由 crates/pgls_configuration/src/splinter/mod.rs 的get_global_ignore_matcher(基于pgls_matcher::Matcher构建全局匹配器)与Rules::get_ignore_matchers(crates/pgls_configuration/src/splinter/rules.rs 中按规则名构建匹配器映射)完成。在 crates/pgls_splinter/src/lib.rs 的run_splinter中,过滤逻辑为:先取诊断的schema.name拼成标识符,依次用全局匹配器、该规则对应的匹配器匹配,命中即剔除。注意:只有带有schema.name元数据的诊断才参与过滤,无此元数据的诊断会保留。

Supabase 专属规则

security组中有相当一部分规则专门针对 Supabase 项目设计,覆盖 Auth schema 暴露、RLS 策略配置、API schema 安全、Supabase 专属扩展等场景。它们在 docs/reference/database_rules.md 中用 ⚡ 标记,在 crates/pgls_splinter/src/rules/security/rls_disabled_in_public.rs 这类规则定义中通过const REQUIRES_SUPABASE: bool = true声明。

关键机制:当数据库中检测不到anonauthenticatedservice_role三个 Supabase 角色时,这些规则会被自动跳过。探测逻辑在 crates/pgls_splinter/src/lib.rs 中,通过 schema cache 的角色列表做全量存在性判断;这也解释了为什么连接非 Supabase 数据库时不会误报这些规则。

CLI 用法

数据库 Linter 可以通过 CLI 独立运行(命令实现见 crates/pgls_cli/src/commands/dblint.rs):

# 运行数据库 linting(使用配置文件中启用的全部规则) postgres-language-server dblint # 只运行指定规则 postgres-language-server dblint --only security/rlsDisabledInPublic # 跳过某些规则 postgres-language-server dblint --skip performance/tableBloat

dblint命令的执行链路:加载配置 → 建立 workspace → 构造PullDatabaseDiagnosticsParams(类别取全部规则)→ 调用workspace.pull_db_diagnostics拉取诊断 → 用Report汇总并输出。退出码规则在enforce_exit_codes中定义:存在 error 级诊断即返回非零退出码;若传了--error-on-warnings,存在 warning 级诊断也会导致非零退出码——这一点对 CI 门禁至关重要。

常用全局选项

以下选项对所有命令生效(详见 docs/reference/cli.md 与 crates/pgls_cli/src/cli_options.rs):

选项说明默认值
--config-path PATH指定配置文件路径或所在目录,禁用默认配置解析自动探测postgres-language-server.jsonc
--max-diagnostics <none\|NUMBER>限制输出的诊断条数,none不限制20
--reporter <json\|json-pretty\|github\|junit\|summary\|gitlab>切换诊断与摘要的输出格式默认终端格式
--diagnostic-level <info\|warn\|error>按最低严重级别过滤诊断info
--error-on-warnings有 warning 诊断时以非零码退出关闭
--skip-errors跳过含语法错误的文件,不输出错误诊断关闭
--verbose输出更多诊断信息与处理文件清单关闭
--log-level <none\|debug\|info\|warn\|error>日志级别,none不输出日志none
--use-server连接已运行的 daemon 服务实例关闭
--colors <off\|force>控制终端着色自动

例如在 CI 中输出 JSON 并限制诊断数量:

postgres-language-server dblint --reporter json --max-diagnostics 100

数据库连接配置

数据库 Linter 必须连接数据库才能分析 Schema。在postgres-language-server.jsonc中用db键配置(见 docs/features/database_linting.md):

{ "db": { "host": "127.0.0.1", "port": 5432, "database": "postgres", "username": "postgres", "password": "postgres" } }

更完整的连接配置可参考 docs/guides/configure_database.md,其中database键下还支持:

  • connTimeoutSecs:连接超时(秒),默认10
  • allowStatementExecutionsAgainst:允许执行语句的 schema 白名单(代码动作相关);
  • disableConnection:完全关闭数据库相关功能。

安全建议:使用只读账号

数据库 Linter 主要依赖对系统目录的读权限。官方指南建议创建一个权限受限的专用账号:

CREATE USER postgres_language_server WITH PASSWORD 'secure_password'; GRANT CONNECT ON DATABASE your_database TO postgres_language_server; GRANT USAGE ON SCHEMA public TO postgres_language_server; GRANT SELECT ON ALL TABLES IN SCHEMA public TO postgres_language_server; GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO postgres_language_server; GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA public TO postgres_language_server;

在 CI 等一次性场景中,也可通过postgres-language-server check sql/ --skip-db跳过数据库连接,仅运行不需要数据库的检查。

实战落地建议

  1. 本地开发:在 IDE 中启用 LSP(需配置好数据库连接),数据库 Linting 诊断会随编辑实时反馈;
  2. CI 门禁:用dblint --error-on-warnings(或依赖默认的 error 退出码)阻断引入rlsDisabledInPublicauthUsersExposed等 Error 级问题,--reporter gitlab/github/junit可对接现有流水线展示;
  3. 对象豁免:对审计表、临时表等已知对象,优先使用全局ignore或按规则options.ignore精准豁免,避免整体关闭规则造成巡检盲区;
  4. 定期巡检:将dblint纳入定时任务,结合tableBloatunusedIndexduplicateIndex等规则持续跟踪线上库的膨胀与冗余索引。

数据库 Linting 的价值在于把"Schema 体检"从人肉巡检变为可重复、可配置、可入 CI 的自动化流程。若需了解全部规则的详细描述与推荐标记,可查阅 docs/reference/database_rules.md;规则级文档(含每条规则的 SQL 查询与配置示例)位于 docs/reference/rules 目录,例如 no-primary-key.md、rls-disabled-in-public.md。

【免费下载链接】postgres_lspA Language Server for Postgres项目地址: https://gitcode.com/GitHub_Trending/po/postgres_lsp

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询