 字符类型)
banCharField 规则深度解析用 VARCHAR/TEXT 替代 CHAR(n) 字符类型【免费下载链接】postgres_lspA Language Server for Postgres项目地址: https://gitcode.com/GitHub_Trending/po/postgres_lsp本指南以 postgres_lsp 的lint/safety/banCharField规则为线索系统讲解该规则的设计动机、触发条件、诊断输出格式与配置方法并结合仓库源码与测试用例深入剖析其底层实现原理。读完本文你将掌握如何在项目中启用该规则、如何读懂它产生的诊断信息以及为什么固定长度的CHAR(n)类型在 Postgres 中应当被避免。规则概览一行表格看清 banCharField属性值诊断类别lint/safety/banCharField规则名称banCharField引入版本vnext当前仓库中的next版本默认严重级别Warning警告是否默认启用recommended否recommended: false灵感来源Squawk 的ban-char-field规则该规则的核心主张非常明确不要使用CHAR(n)或CHARACTER(n)类型而应改用VARCHAR或TEXT存储可变长度的字符数据。这条规则被归类在safety安全性诊断组下与 数据库规则索引 中的其他安全类规则并列。为什么禁用 CHAR 类型空间填充引发的三类问题规则的 Description 部分给出了三条技术依据这些结论与 PostgreSQL 官方对字符类型的描述一致固定长度与空格填充CHAR(n)是定长类型存储时不足n的字符串会被自动用空格补齐到固定长度。这会导致存储的值与逻辑上的值不一致——比较字符串或拼接值时往往产生意料之外的结果。例如abc存入CHAR(5)后实际存储为abc 与abc直接比较时 Postgres 虽会做空格修剪处理但在拼接、LIKE匹配、取出后二次处理等场景中极易踩坑。存储空间浪费当实际值长度明显小于声明的n时每个值都要占用n字节的空间在 PostgreSQL 中bpchar使用n4字节的定长存储布局短值越多浪费越严重。语义误导CHAR(n)给人的直觉是限制最大长度但它的真实语义是固定长度二者本质不同容易让后续维护者误用。因此规则给出的建议是使用VARCHAR可变长度带可选长度上限或TEXT无限长度替代CHAR。如果你只需要长度约束VARCHAR(n)是更安全的选择如果需要无限长度则直接用TEXT。触发场景哪些 SQL 会被判定为违规从 规则实现 的源码看该规则目前只针对两类 DDL 语句中的列定义进行检查CREATE TABLE语句遍历CreateStmt的table_elts对每个ColumnDef列定义检查其类型名源码 L50-L58。ALTER TABLE ... ADD COLUMN语句遍历AlterTableStmt的cmds仅当子命令类型为AtAddColumn添加列时检查新列定义源码 L61-L72。也就是说该规则不会检查ALTER COLUMN ... TYPE之类的类型变更语句也不检查函数参数、复合类型等其他出现CHAR的上下文——这些属于规则当前的覆盖边界使用时需留意。类型名匹配三种写法都会被捕获类型检测逻辑位于check_column_for_char_type函数源码 L78-L104。它将列的类型名节点取出并转小写后与以下三个字符串逐一比对匹配的字符串对应 SQL 写法说明charchar(n)SQL 标准写法charactercharacter(n)char的完整拼写bpcharbpchar(n)PostgreSQL 内部存储名bpchar即 blank-padded char第三个匹配项很有价值bpchar是 PostgreSQL 内部对CHAR类型的存储名称。即使开发者显式写出bpchar(5)这种内部类型名常见于从pg_attribute系统表拷出的类型定义规则同样能命中。仓库测试用例 bpchar.sql 专门验证了这一场景。测试用例印证仓库在crates/pgls_analyser/tests/specs/safety/banCharField/目录下提供了四组规格测试SQL 输入 snap 快照basic.sqlCREATE TABLE中的char(100)列触发诊断bpchar.sqlCREATE TABLE中的bpchar(5)列触发诊断alter_table.sqlALTER TABLE users ADD COLUMN code character(10)触发诊断varchar_valid.sql使用varchar(100)的合法写法不产生诊断。这些测试文件头部以-- expect_lint/safety/banCharField注释声明期望的诊断类别由测试框架自动断言是验证规则行为最直接的依据。诊断输出格式解读违规示例Invalid以下建表语句使用了char(100)CREATE TABLE core_bar ( id serial NOT NULL PRIMARY KEY, alpha char(100) NOT NULL );CLI 或 LSP 会输出如下格式的诊断code-block.sql:1:1 lint/safety/banCharField ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! CHAR type is discouraged due to space padding behavior. 1 │ CREATE TABLE core_bar ( │ ^^^^^^^^^^^^^^^^^^^^^^^^^ 2 │ id serial NOT NULL PRIMARY KEY, 3 │ alpha char(100) NOT NULL 4 │ ); │ ^^ 5 │ i CHAR types are fixed-length and padded with spaces, which can lead to unexpected behavior. i Use VARCHAR or TEXT instead for variable-length character data.诊断信息包含三个层次均可在 规则实现 中对应到主消息!行CHAR type is discouraged due to space padding behavior.——直接点出空格填充这一核心原因详情i行CHAR types are fixed-length and padded with spaces, which can lead to unexpected behavior.——解释潜在的行为异常修复建议第二个i行Use VARCHAR or TEXT instead for variable-length character data.——给出明确的替代方案。注意主消息标记在CREATE TABLE语句起始位置同时列名alpha处也有^^精确指向方便在长表结构中快速定位到具体违规列。合法示例ValidCREATE TABLE core_bar ( id serial NOT NULL PRIMARY KEY, alpha varchar(100) NOT NULL );将char(100)换成varchar(100)后规则不再产生任何诊断——既保留了 100 字符的长度上限又避免了空格填充问题。如何配置 banCharField 规则配置文件方式在项目的postgres-language-server.jsonc或其他支持位置的配置文件中启用该规则{ linter: { rules: { safety: { banCharField: error } } } }该规则位于safety规则组下因此配置层级必须是linter.rules.safety.banCharField。取值说明取值效果off关闭该规则默认状态因为recommended: falsewarn以警告级别提示error以错误级别提示可配合--error-on-warnings等 CI 开关阻断构建由于规则默认recommended: false它不会随safety组的推荐规则集自动启用需要显式配置。从 配置源码 可以看到配置结构体ban_char_field字段的类型为OptionRuleConfigurationpgls_analyser::options::BanCharField其中BanCharField选项类型为空结构type Options ()见 规则实现 L45即该规则不接受任何附加参数——启用与否就是全部可配置项行为完全固定。CLI 方式若未在配置文件中启用也可以通过命令行直接按诊断类别过滤postgres-language-server check --rulessafety/banCharField path/to/migrations具体 CLI 参数形态以 CLI 参考文档 为准。源码级实现原理整个规则的实现链路非常清晰分为三层规则声明宏使用declare_lint_rule!声明规则元信息包括名称banCharField、版本next、严重级别Warning、来源RuleSource::Squawk(ban-char-field)并把规则文档注释作为描述L6-L42。规则注册在 lint/safety.rs 的Safety规则组中注册BanCharField使其可以通过lint/safety/banCharField类别被引用。诊断生成run方法基于pgls_query提供的语法树节点CreateStmt/AlterTableStmt/ColumnDef做结构化遍历而非正则匹配文本因此对大小写混写如ChAr、模式限定名、注释干扰等情况都有稳定的识别能力类型名统一转小写后比较见 L86。这种基于 AST 的规则实现方式意味着只要 SQL 能被pgls_query解析为合法的语法树规则就能给出精确到列的诊断定位这也是它与文本搜索式 linter的本质区别。最佳实践建议新表一律使用varchar/text绝大多数业务字段都不需要定长语义varchar(n)足够表达长度约束迁移存量char列对历史遗留的char(n)列评估后通过ALTER COLUMN ... TYPE varchar(n)平滑迁移在 CI 中开启该规则将banCharField设为error配合postgres-language-server check在提交前拦截新增的CHAR类型避免问题随迁移文件流入生产。相关资源规则文档原文docs/reference/rules/ban-char-field.md规则实现源码crates/pgls_analyser/src/lint/safety/ban_char_field.rs规则组注册crates/pgls_analyser/src/lint/safety.rs配置结构定义crates/pgls_configuration/src/linter/rules.rs规格测试用例crates/pgls_analyser/tests/specs/safety/banCharField/目录basic / bpchar / alter_table / varchar_valid完整规则索引docs/reference/rules.md【免费下载链接】postgres_lspA Language Server for Postgres项目地址: https://gitcode.com/GitHub_Trending/po/postgres_lsp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考