使用类型修饰符的数据类型的结果令人惊讶


11

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

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

1.类型转换在某些情况下保留类型修饰符

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

SELECT 'vc8'::varchar(8)::varchar

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

2.数组串联在某些情况下会丢失类型修饰符

不需要,因此在另一侧犯了错误:

SELECT ARRAY['vc8']::varchar(8)[]
     , ARRAY['vc8']::varchar(8)[] || 'vc8'::varchar(8)

第一个表达式产生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');

尽管此rCTE适用于该varcharvc,但不适用于该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;
错误:递归查询“ cte”列1在非递归术语中具有类型字符varianting(8)[],但总体上具有类型字符variable []  
提示:将非递归项的输出强制转换为正确的类型。位置:103

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

普通UNION查询不会出现相同的问题:它会查询没有修饰符的类型,从而保证保留所有信息。但是rCTE更挑剔。

另外,使用/ max(vc8)代替更常用的方法也不会遇到问题,因为和朋友可以立即解决(或没有修饰符的相应基本类型)。ORDER BYLIMIT 1max()text

SQL Fiddle演示了三件事:

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

小提琴是针对9.3版的。对于第9.4.4页,我在本地得到相同的结果。

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

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

问题

这三个问题在一起使事情变得一团糟。
确切地说,问题1.没有直接涉及,但它以非递归术语(ARRAY[vc8]::varchar[]或类似术语)的强制转换破坏了看似明显的修复,这增加了混乱。
这些项目中的哪一个是错误,故障或应有的状态?
我是否缺少某些东西,还是应该报告错误?


这当然似乎相当可疑。我怀疑现有的联合查询的向后兼容性可能会起作用。
Craig Ringer 2015年

@CraigRinger:即使请求,我也看不出为什么数组级联不需要修饰符而丢弃修饰符,而强制转换则不需要。为何rCTE必须比简单UNION查询更严格(不够智能)。难道我们一次发现了三个独立的小错误?(经过数月未找到此类信息。)您认为应该将其中哪些记录为错误?
Erwin Brandstetter

Answers:


1

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.