- 文档
- 教程
- 知识库
【免费下载链接】til
:memo: Today I Learned
PostgreSQL 对表名、列名、约束名等所有标识符施加了 63 字节的硬性长度上限,超长标识符并不会报错,而是被自动截断,这一行为在日常建表、加约束时极易被忽视并引发隐患。本文将结合本仓库的 PostgreSQL 系列笔记,完整解析 63 字节限制的来龙去脉、截断时的 NOTICE 提示、自动生成标识符的“保后缀”策略,以及该上限的源码出处NAMEDATALEN - 1。
63 字节:PostgreSQL 标识符的硬性上限
在 PostgreSQL 中,所有标识符——表名(table names)、列名(column names)、约束名(constraint names)等——的长度都被限制为最多 63 字节。这个限制并不是“超过就报错”,而是更隐蔽:可以写入超过 63 字符的标识符,但 PostgreSQL 会将其截断到允许的 63 字节长度,并给出一条 NOTICE 提示。
这与本仓库中另一则笔记 表名默认被当作小写处理 所揭示的行为类似:PostgreSQL 会在处理标识符时对名称进行规范化(折叠大小写、截断超长部分),因此开发者实际写下的名字与数据库最终存储的名字可能并不一致。理解这套标识符处理规则,是避免“名字对不上”“约束找不到”等困惑的前提。
超长标识符不会报错,而是被截断
直接看效果最直观。在 psql 中执行下面这条alter table,尝试添加一个明显超过 63 字节的 CHECK 约束名:
> alter table articles add constraint this_constraint_is_going_to_be_longer_than_sixty_three_characters_id_idx check (char_length(title) > 0); NOTICE: identifier "this_constraint_is_going_to_be_longer_than_sixty_three_characters_id_idx" will be truncated to "this_constraint_is_going_to_be_longer_than_sixty_three_characte" ALTER TABLE两个关键信息值得注意:
- 操作本身成功:命令返回
ALTER TABLE,约束照常创建,全程没有报错,只有一条NOTICE。 - NOTICE 明确告知截断结果:PostgreSQL 会明确指出超长标识符将被截断为哪个名字。上例中原本 78 字节的约束名,被截断为 63 字节的
this_constraint_is_going_to_be_longer_than_sixty_three_characte——注意末尾少了s_id_idx几个字符。
这种“静默截断 + NOTICE 提醒”的设计意味着:只要没盯着服务端日志或 psql 输出,你可能永远不会发现约束名、索引名已经被悄悄改写了。
截断后的名字才是真实存在的名字
截断一旦发生,之后所有针对该对象的操作都必须使用截断后的名字。例如删除约束时必须写:
alter table articles drop constraint this_constraint_is_going_to_be_longer_than_sixty_three_characte;如果按自己书写的全名去drop constraint,会得到 “constraint does not exist” 之类的错误——因为数据库里存的是截断后的版本。
想确认表上约束的真实存储名字,可以查询系统目录pg_constraint,并用pg_get_constraintdef(oid)重建约束定义。仓库笔记 Show Reconstructed Constraints For A Table 给出了完整的示例:
> select conname, pg_get_constraintdef(oid) from pg_constraint where conrelid = 'reading_statuses'::regclass; conname | pg_get_constraintdef -------------------------------------+---------------------------------------------------------------- reading_statuses_pkey | PRIMARY KEY (id) fk_rails_17ee7cb2c4 | FOREIGN KEY (user_id) REFERENCES users(id) fk_rails_0d3729339f | FOREIGN KEY (book_id) REFERENCES books(id) reading_statuses_valid_status_check | CHECK (...) (4 rows)通过conname看到的名字,就是经过 PostgreSQL 规范化(含截断)后的最终名字,也是你在所有后续 SQL 中真正需要引用的名字。
自动生成的标识符:从中间截断,保住_fkey等后缀
超长标识符不仅会发生在开发者手写的名字上,PostgreSQL 自动生成的标识符同样受 63 字节限制。最常见的场景是外键约束:当你在alter table中省略约束名时,PostgreSQL 会按约定自动生成xxx_fkey这类名字。
仓库笔记 Add Foreign Key Constraint Without A Full Lock 展示了这种命名约定:
alter table books add constraint fk_books_authors foreign key (author_id) references authors(id) not valid;这里手写了fk_books_authors;但如果省略constraint子句,PostgreSQL 生成的默认名通常是<表名>_<列名>_fkey,而在 Rails 等框架下则常见fk_rails_<hash>这样的短名(如上面的fk_rails_17ee7cb2c4)。
问题在于:当表名、列名本身较长时,自动拼出的_fkey全名可能轻松超过 63 字节。此时 PostgreSQL 的截断策略与手写标识符不同——它会在中间某个位置截断,而不是从尾部硬切,目的是保住约定俗成的后缀,例如以_fkey结尾。这样生成的名字依然符合“一眼看出是外键约束”的命名惯例。
这也解释了为什么在真实生产库中,你会看到诸如books_authors_very_long_column_name_fk(截断版)这样的名字——命名习惯被保留了,但可读性已经大打折扣。
63 字节的由来:NAMEDATALEN - 1
63 这个数字并非拍脑袋定下的,它直接来自 PostgreSQL 源码中的一个编译期常量:
NAMEDATALEN - 1- 默认情况下
NAMEDATALEN的值为64,因此标识符上限为 63。 - 这个值是编译期硬编码的,而不是运行时配置项,无法通过
postgresql.conf之类的配置文件调整。 - 如果确实需要,可以在 PostgreSQL 源码中修改
NAMEDATALEN(位于源码的pg_config_manual.h中)后重新编译数据库。
之所以预留 1 字节的余量,是为了容纳字符串的结尾空字符(\0):即内部缓冲区大小为NAMEDATALEN(64 字节),其中最多 63 字节用于标识符本身。正如原文所说,“Yay, open-source database implementations”——开源数据库的好处在于,这种底层限制的出处是透明的,需要时甚至可以亲自改掉它。不过在实际生产环境中,修改后需要重新编译并可能导致与生态工具(迁移工具、ORM 生成的名称)的不兼容,绝大多数团队不应走到这一步。
注意:上限单位是字节,不是字符
限制单位是63 字节而非“63 个字符”。对于纯 ASCII 标识符,1 字节 = 1 字符,63 字符即上限;但一旦使用多字节字符(如中文、日文、emoji 命名的标识符),每个字符可能占用 2~4 字节,实际可用的字符数会显著少于 63。截断也是按字节进行的,因此含多字节字符的超长标识符被截断后,其边界不一定落在完整的字符边界上。实用建议:无论是 ASCII 还是多字节命名,都留足余量,远离 63 字节这条红线。
工程实践:如何避开 63 字节陷阱
结合本仓库 PostgreSQL 相关笔记的实践,可以从以下几个层面规避截断带来的麻烦:
- 命名从源头保持精简:约束名、索引名、外键名都采用“表名 + 关键列 + 类型后缀”的短命名,例如
fk_books_authors、uq_users_email,把长度控制在 63 字节以内,让截断根本不发生。 - 沿用 snake_case 小写命名:仓库笔记 Table Names Are Treated As Lower-Case By Default 强调,PostgreSQL 默认把未加引号的标识符折叠为小写。保持全小写 snake_case 既能规避大小写混淆,也让名字更短、更符合约定。
- 了解框架的自动命名:Rails 等框架默认生成
fk_rails_<16 位 hash>这类固定长度短名(见上文pg_constraint查询结果),天然不会超长;手写长表名与长列名组合时,才最容易踩中自动拼接超长的坑。 - 改名前留意连带命名:仓库笔记 Renaming A Table 提醒,
alter table ... rename to ...会更新外键引用,但与旧表同名的序列、约束等名字不再遵循原约定——这些名字若因重命名变得更长,同样可能触及 63 字节截断,需一并检查。 - 用系统目录自查:建完约束后,通过
pg_constraint.conname与pg_get_constraintdef()核对实际存储的约束名(方法见 Show Reconstructed Constraints For A Table),确保迁移脚本后续引用的名字与库中真实名字一致。
小结
PostgreSQL 的标识符上限是 63 字节,来源于编译期常量NAMEDATALEN - 1(默认NAMEDATALEN = 64)。超长标识符不会被拒绝,而是被截断并给出 NOTICE;自动生成的标识符(如外键的_fkey名)则会从中间截断以保住后缀约定。理解这一机制,能帮你解释许多“名字对不上”的怪象,并在设计表结构、编写迁移脚本时提前规避。更多 PostgreSQL 命名与约束相关的实践,可继续翻阅本仓库的 PostgreSQL 笔记目录。
- 文档
- 教程
- 知识库
【免费下载链接】til
:memo: Today I Learned
相关推荐
PostgreSQL技术解析:标识符最大长度限制为63字节
PostgreSQL技术解析:标识符最大长度限制为63字节 标识符长度限制概述 在PostgreSQL数据库中,各种标识符(包括表名、列名、约束名等)都有一个明
文档教程知识库curl --max-filesize 使用详解:文件大小上限限制机制、字节后缀语法与退出码 63
curl max filesize 使用详解:文件大小上限限制机制、字节后缀语法与退出码 63 max filesize 是 curl 命令行工具中用于限制下载
CLI网络通信零运行时原子 CSS 框架:vanilla-extract Sprinkles 完整实战指南
零运行时原子 CSS 框架:vanilla extract Sprinkles 完整实战指南 vanilla extract 生态中, Sprinkles ht
前端开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考