为什么atttypmod与character_maximum_length不同?

7

我正在将一些information_schema的查询转换为系统目录查询,但字符最大长度的结果不同。

SELECT column_name, 
    data_type , 
    character_maximum_length AS "maxlen"
FROM information_schema.columns 
WHERE table_name = 'x'

返回我期望的结果,例如:

city    character varying   255
company character varying   1000

等价目录查询

SELECT attname,
       atttypid::regtype  AS datatype,
       NULLIF(atttypmod, -1) AS maxlen
FROM   pg_attribute
WHERE  CAST(attrelid::regclass AS varchar) = 'x'
AND    attnum > 0
AND    NOT attisdropped

看起来返回每个长度+4:

city    character varying   259
company character varying   1004

为什么会有这个差异?总是从结果中减去4安全吗?
1个回答

9
你可以说对于类型为charvarchar,从结果中减去4是安全的。在底层,information_schema.columns视图调用了一个名为information_schema._pg_char_max_length的函数(这是你们的区别,因为你不这样做),其函数体如下:
CREATE OR REPLACE FUNCTION information_schema._pg_char_max_length(typid oid, typmod integer)
 RETURNS integer
 LANGUAGE sql
 IMMUTABLE PARALLEL SAFE STRICT
AS $function$SELECT
  CASE WHEN $2 = -1 /* default typmod */
       THEN null
       WHEN $1 IN (1042, 1043) /* char, varchar */
       THEN $2 - 4
       WHEN $1 IN (1560, 1562) /* bit, varbit */
       THEN $2
       ELSE null
  END$function$

话虽如此,对于字符和变长字符类型,它总是会减去4个字节。 这使得您的查询不等同于实际需要连接pg_type以确定列的typid并将值包装在函数中以返回正确值。这是因为还有比这更多的事情要考虑。如果您希望简化,可以在不连接任何表(但不完全可靠)的情况下完成:

SELECT attname,
       atttypid::regtype  AS datatype,
       NULLIF(information_schema._pg_char_max_length(atttypid, atttypmod), -1) AS maxlen
FROM   pg_attribute
WHERE  CAST(attrelid::regclass AS varchar) = 'x'
AND    attnum > 0
AND    NOT attisdropped

这应该可以满足您的需求。如果您希望进一步了解此事,请参考查看 information_schema.columns 的定义。


谢谢。atttypemod是指字节大小而不是字符串长度的引用,因此它+4是因为字符串在内部存储的方式吗?也许它在额外的字节中有排序信息? - Matt S
1
据我所知,字符串终止符占用了1个字节(4个字符)。 这可能是一个参考链接:https://www.postgresql.org/docs/current/static/datatype-character.html - Kamil Gosciminski

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接