Обговорюючи рекурсивне рішення 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 працює для 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
запити. Чи може бути, що ми знайшли відразу три незалежних клопа? (Через місяці та місяці подібного не знайдено.) Хто з них, на вашу думку, має бути поданий як помилка?