You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 02:17:49