PostgreSQL查询pg_type与pg_attribute时文本截断问题解析
问题:PostgreSQL中CASE语句处理复合类型时文本被截断的原因
我尝试从数据库schema生成TypeScript类型,需要遍历所有自定义类型来创建TypeScript类型定义。但使用CASE语句同时处理复合类型和域类型时,复合类型的文本被截断。
最小复现代码(含问题的SQL)
DROP TYPE IF EXISTS my_type CASCADE; CREATE TYPE my_type AS ( attribute1 TEXT , attribute2 TEXT , attribute3 TEXT , attribute4 TEXT , attribute5 TEXT , attribute6 TEXT , attribute7 TEXT , attribute8 TEXT , attribute9 TEXT , attribute10 TEXT , attribute11 TEXT ); CREATE DOMAIN my_domain AS my_type; SELECT CASE WHEN t.typtype='d' THEN ( SELECT base.typname FROM pg_type base WHERE t.typbasetype=base.oid ) WHEN t.typtype='c' THEN array_to_string ( ARRAY ( SELECT a.attname || ': ' || at.typname FROM pg_attribute a LEFT JOIN pg_type at ON a.atttypid = at.oid WHERE t.typrelid = a.attrelid AND a.attnum > 0 ) , ',' ) ELSE '' END FROM pg_type t WHERE typname='my_type' OR typname='my_domain';
执行输出(复合类型结果被截断)
"case" "attribute11: text,attribute10: text,attribute9: text,attribute8" "my_type"
移除域类型查询后的代码(无截断)
SELECT CASE WHEN t.typtype='d' THEN ( 'HELLO' ) WHEN t.typtype='c' THEN array_to_string ( ARRAY ( SELECT a.attname || ': ' || at.typname FROM pg_attribute a LEFT JOIN pg_type at ON a.atttypid = at.oid WHERE t.typrelid = a.attrelid AND a.attnum > 0 ) , ',' ) ELSE '' END FROM pg_type t WHERE typname='my_type' OR typname='my_domain';
执行输出(完整结果)
"case" "attribute11: text,attribute10: text,attribute9: text,attribute8: text,attribute7: text,attribute6: text,attribute5: text,attribute4: text,attribute3: text,attribute2: text,attribute1: text" "HELLO"
请问该截断现象的机制是什么?
答案
这个截断问题的核心是PostgreSQL CASE表达式的返回类型推导规则:
PostgreSQL会根据CASE表达式中第一个分支的返回类型,自动确定整个CASE表达式的返回类型。在你的问题代码中:
- 第一个分支(
typtype='d',域类型)返回的是pg_type.typname,这个字段的类型是name——PostgreSQL内置的固定长度类型,最大长度为63字节。 - 当第二个分支(复合类型)生成的字符串长度超过63字节时,就会被自动截断到63字节,匹配第一个分支的类型长度限制。
你可以验证一下截断后的字符串长度:"attribute11: text,attribute10: text,attribute9: text,attribute8"的字节数刚好是63,完全符合name类型的长度限制。
解决办法
只需要把第一个分支的结果显式转换为text类型,让CASE表达式的返回类型变为无长度限制的text,就能避免截断:
SELECT CASE WHEN t.typtype='d' THEN ( SELECT base.typname::text FROM pg_type base WHERE t.typbasetype=base.oid ) WHEN t.typtype='c' THEN array_to_string ( ARRAY ( SELECT a.attname || ': ' || at.typname FROM pg_attribute a LEFT JOIN pg_type at ON a.atttypid = at.oid WHERE t.typrelid = a.attrelid AND a.attnum > 0 ) , ',' ) ELSE ''::text END FROM pg_type t WHERE typname='my_type' OR typname='my_domain';
执行这个修改后的SQL,复合类型的结果就会完整返回,不会再被截断。
内容的提问来源于stack exchange,提问作者mushishi78
相关产品推荐
相关产品推荐

