Удивительные результаты для типов данных с модификатором типа

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)

1-е выражение дает varchar(8)[]ожидаемый результат.
Но 2-й, после конкатенации, другой 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 работает для 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;
ОШИБКА: в рекурсивном запросе столбец "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)(с модификатором).

Скрипка для стр 9.3. Я получаю те же результаты локально для pg 9.4.4.

Я создал таблицы из демонстрационных выражений, чтобы показать точный тип данных, включая модификатор. В то время как pgAdmin показывает эту информацию из коробки, она не доступна из sqlfiddle. Примечательно, что он также недоступен в psql(!). Это известный недостаток в psql, и возможное решение было обсуждено на pgsql-хакерах ранее - но пока не реализовано. Это может быть одной из причин, по которой проблема еще не обнаружена и не устранена.

На уровне SQL вы можете использовать, pg_typeof()чтобы получить тип (но не модификатор).

Вопросы

Вместе эти 3 вопроса создают беспорядок.
Точнее говоря, проблема 1. непосредственно не затрагивается, но она разрушает кажущееся очевидным исправление с использованием нерекурсивного термина: ARRAY[vc8]::varchar[]или подобного, что добавляет путаницу.
Какой из этих предметов является ошибкой, глюк или просто как это должно быть?
Я что-то упустил или мы должны сообщить об ошибке?

Эрвин Брандштеттер
источник
Это, конечно, кажется довольно подозрительным. Я подозреваю, что обратная совместимость для существующих запросов объединения может сыграть свою роль.
Крейг Рингер,
@CraigRinger: я не понимаю, почему конкатенация массивов удаляет модификатор без необходимости, а приведение - нет, хотя и запрашивается. Не то, почему rCTE должен быть более строгим (менее умным), чем простые UNIONзапросы. Может ли быть так, что мы нашли три независимых маленьких ошибки одновременно? (После месяцев и месяцев такой находки не было.) Как вы думаете, что из этого следует рассматривать как ошибку?
Эрвин Брандштеттер,

Ответы:

1

Это связано с тем, что атрибуты отношения (определенные в pg_classи pg_attributeили определенные динамически из selectоператора) поддерживают модификаторы (через pg_attribute.atttypmod), а параметры функции - нет. Модификаторы теряются при обработке через функции, и поскольку все операторы обрабатываются через функции, модификаторы также теряются при обработке операторами.

Функции с выходными значениями, или которые возвращают наборы записей или эквивалент returns table(...), также не могут сохранять любые модификаторы, включенные в определение. Тем не менее, таблицы, которые return setof <type>будут сохранять (на самом деле, вероятно, typecast) любые модификаторы, определенные для typeв pg_attribute.

Ziggy Crueltyfree Zeitgeister
источник