max_identifier_length 报告的数字是 63,但数字本身不是重点。PostgreSQL 与 MySQL、SQL Server、Oracle 不同:超长标识符不是报错,而是静默截断。
截断发生在词法分析器的 truncate_identifier(),按 63 字节而非字符切割,适用于所有标识符:表、列、索引、约束、schema、角色、数据库、函数、游标、保存点、LISTEN 通道。引号保留大小写,但无助于长度。
失败模式是碰撞。两个前 63 字节相同的名字被视为同一个,生成名称时变化部分在末尾,最容易踩坑。示例:60 字符父表名的每日分区,_p2024_01_01 和 _p2024_01_02 都被截成同一名字,第二个报 already exists。更糟的是 DROP TABLE 解析到同一 63 字节,可能删掉今早刚建的分区,且无报错。
PostgreSQL 自身生成的名称安全:makeObjectName() 缩短表名和列名部分,碰撞时加计数器重试。外部工具处理不一:pg_partman 会修剪父名腾出空间;Django 自 2010 年报告 63 并哈希索引名尾部;Rails 7.1 起改用哈希,Action Cable 适配器今年才修复按字符而非字节计数的 bug。
可以提升:NAMEDATALEN 改为 128 可得 127 字节名称,但需 initdb,pg_upgrade 拒绝迁移,C 扩展需全部重编。邮件列表 2012、2017、2021 年都提过,答案不变:name 是定宽 64 字节,翻倍会让所有目录行和 syscache 翻倍。
建议把 63 当预算,后缀优先:约定追加 _p2024_01_01 或 _pkey 时,基础名超过 45 字符就是隐患。脚本生成名称先检查 octet_length(candidate) 与 max_identifier_length 比较,再用查询找出已有 63 字节的名称,确认是否被截断。SQL 标准允许 128,Oracle 12.2、SQL Server 早已支持,MySQL 允许 64 字符。PostgreSQL 是短的,也会保持短的。参数本身不能设置,能做的只有规划命名。
🔗 原文:postgr.es/p/9uq