Дивовижні результати для типів даних з модифікатором типу


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 працює для 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>збережуть ( на насправді, ймовірно , надрукований в) будь-які модифікатори , певні для typeв pg_attribute.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.