При обсуждении рекурсивного решения 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 вещи:
- Ряд примеров выражений, включая удивительные результаты.
- Простой rCTE, который работает с
varchar
(без модификатора). - Тот же самый rCTE, вызывающий исключение для
varchar(n)
(с модификатором).
Скрипка для стр 9.3. Я получаю те же результаты локально для pg 9.4.4.
Я создал таблицы из демонстрационных выражений, чтобы показать точный тип данных, включая модификатор. В то время как pgAdmin показывает эту информацию из коробки, она не доступна из sqlfiddle. Примечательно, что он также недоступен в psql
(!). Это известный недостаток в psql, и возможное решение было обсуждено на pgsql-хакерах ранее - но пока не реализовано. Это может быть одной из причин, по которой проблема еще не обнаружена и не устранена.
На уровне SQL вы можете использовать, pg_typeof()
чтобы получить тип (но не модификатор).
Вопросы
Вместе эти 3 вопроса создают беспорядок.
Точнее говоря, проблема 1. непосредственно не затрагивается, но она разрушает кажущееся очевидным исправление с использованием нерекурсивного термина: ARRAY[vc8]::varchar[]
или подобного, что добавляет путаницу.
Какой из этих предметов является ошибкой, глюк или просто как это должно быть?
Я что-то упустил или мы должны сообщить об ошибке?
UNION
запросы. Может ли быть так, что мы нашли три независимых маленьких ошибки одновременно? (После месяцев и месяцев такой находки не было.) Как вы думаете, что из этого следует рассматривать как ошибку?