带有类型修饰符的数据类型的惊人结果

Erw*_*ter 11 postgresql datatypes cte debugging cast

在讨论这个问题的递归 CTE 解决方案时:

@ypercube偶然发现了一个令人惊讶的异常,这导致我们调查类型修饰符的处理。我们发现了令人惊讶的行为。

1. 类型转换在某些上下文中保留了类型修饰符

即使被指示不要这样做。最基本的例子:

SELECT 'vc8'::varchar(8)::varchar
Run Code Online (Sandbox Code Playgroud)

人们可能会期望varchar(没有修饰符),至少我会。但结果是varchar(8)(带修饰符)。下面的小提琴中有许多相关的案例。

2. 数组连接在某些情况下失去了类型修饰符

不需要,所以这在相反的一面出错:

SELECT ARRAY['vc8']::varchar(8)[]
     , ARRAY['vc8']::varchar(8)[] || 'vc8'::varchar(8)
Run Code Online (Sandbox Code Playgroud)

第一个表达式varchar(8)[]按预期产生。
但是第二个,在连接另一个之后varchar(8)被淡化到只是varchar[](没有修饰符)。来自array_append(),下面小提琴中的示例的类似行为。

在大多数情况下,所有这些都无关紧要。Postgres 不会丢失数据,并且当分配给列时,该值无论如何都会被强制转换为正确的类型。然而,相反方向的错误最终会导致一个令人惊讶的异常:

3. 递归 CTE 要求数据类型完全匹配

鉴于此简化表:

CREATE TABLE a (
  vc8  varchar(8)  -- with modifier
, vc   varchar     -- without  
);
INSERT INTO a VALUES  ('a',  'a'), ('bb', 'bb');
Run Code Online (Sandbox Code Playgroud)

虽然此 rCTE 对varchar列有效vc,但对varchar(8)列失败vc8

WITH RECURSIVE cte AS (
   (
   SELECT ARRAY[vc8] AS arr  -- produces varchar(8)[]
   FROM   a
   ORDER  BY vc8
   LIMIT 1
   )

   UNION ALL
   (
   SELECT a.vc8 || c.arr  -- produces varchar[] !!
   FROM   cte c
   JOIN   a ON a.vc8 > c.arr[1]
   ORDER  BY vc8
   LIMIT 1
   )
   )
TABLE  cte;
Run Code Online (Sandbox Code Playgroud)
错误:递归查询“cte”第 1 列在非递归项中具有类型字符变化 (8)[] 但总体类型字符变化 []  
提示:将非递归项的输出转换为正确的类型。职位:103

一种快速的解决方法是强制转换为text.

一个普通的UNION查询不会出现同样的问题:它满足于没有修饰符的类型,它保证保留所有信息。但 rCTE 更挑剔。

此外,您不会遇到更常用max(vc8)而不是ORDER BY/ 的问题LIMIT 1,因为max()朋友们会立即适应text(或没有修饰符的相应基类型)。

SQL Fiddle展示了 3 件事:

  1. 一系列示例表达式,包括令人惊讶的结果。
  2. 一个简单的 rCTE,可以使用varchar(不带修饰符)。
  3. 相同的 rCTE 为varchar(n)(with 修饰符)引发异常。

小提琴适用于第 9.3 页。对于第 9.4.4 页,我在本地得到了相同的结果。

我从演示表达式创建了表,以便能够显示包括修饰符在内的确切数据类型。虽然 pgAdmin 开箱即用地显示了此信息,但它无法从 sqlfiddle 获得。值得注意的是,它在psql(!) 中也不可用。这是 psql 中已知的缺点,之前已经在 pgsql-hackers 上讨论过一个可能的解决方案- 但尚未实现。这可能是尚未检测到并修复该问题的原因之一。

在 SQL 级别,您可以使用pg_typeof()获取类型(但不能使用修饰符)。

问题

这三个问题合在一起就搞砸了。
准确地说,问题1.不直接涉及,但它破坏了看似明显的修复,在非递归术语中使用强制转换:ARRAY[vc8]::varchar[]或类似的,这增加了混乱。
这些项目中的哪一个是错误、故障或它应该是怎样的?
我是否遗漏了什么或者我们应该报告一个错误?

Eze*_*nay 2

这是由于关系属性pg_class(在和中定义pg_attribute,或从select语句动态定义)支持修饰符(通过pg_attribute.atttypmod),而函数参数则不支持。当通过函数处理时,修饰符会丢失,并且由于所有运算符都是通过函数处理的,因此当由运算符处理时,修饰符也会丢失。

具有输出值或返回记录集或等效函数的函数returns table(...)也无法保留定义中包含的任何修饰符。但是,return setof <type>将保留(实际上,可能类型转换为)为typein定义的任何修饰符的表pg_attribute