En discutant d'une solution CTE récursive pour cette question:
@ypercube est tombé sur une exception surprenante, ce qui nous a amenés à étudier la gestion des modificateurs de type. Nous avons trouvé un comportement surprenant.
1. La conversion de type conserve le modificateur de type dans certains contextes
Même lorsqu'on lui a demandé de ne pas le faire. L'exemple le plus élémentaire:
SELECT 'vc8'::varchar(8)::varchar
On pourrait s'y attendre varchar
(pas de modificateur), du moins je le ferais. Mais le résultat est varchar(8)
(avec modificateur). De nombreux cas liés dans le violon ci-dessous.
2. La concaténation de tableau perd le modificateur de type dans certains contextes
Sans besoin, cela se trompe donc du côté opposé:
SELECT ARRAY['vc8']::varchar(8)[]
, ARRAY['vc8']::varchar(8)[] || 'vc8'::varchar(8)
La 1ère expression donne varchar(8)[]
comme prévu.
Mais le 2e, après avoir enchaîné un autre, varchar(8)
est édulcoré à juste varchar[]
(pas de modificateur). Comportement similaire à partir d' array_append()
exemples dans le violon ci-dessous.
Tout cela n'a pas d'importance dans la plupart des contextes. Postgres ne perd pas de données et lorsqu'elle est affectée à une colonne, la valeur est de toute façon contrainte au bon type. Cependant , l'erreur dans des directions opposées aboutit à une exception surprenante:
3. CTE récursif exige que les types de données correspondent exactement
Compte tenu de ce tableau simplifié:
CREATE TABLE a (
vc8 varchar(8) -- with modifier
, vc varchar -- without
);
INSERT INTO a VALUES ('a', 'a'), ('bb', 'bb');
Bien que ce rCTE fonctionne pour la varchar
colonne vc
, il échoue pour la varchar(8)
colonne 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;
ERREUR: la colonne 1 de la requête récursive "cte" a un caractère de type variant (8) [] en terme non récursif mais un caractère de type variant [] dans l'ensemble Astuce: transtypez la sortie du terme non récursif dans le type correct. Position: 103
Une solution de contournement rapide consisterait à effectuer un cast vers text
.
Une UNION
requête simple ne pose pas le même problème: elle se contente du type sans modificateur, ce qui garantit de conserver toutes les informations. Mais le rCTE est plus pointilleux.
En outre, vous ne rencontreriez pas de problèmes avec le plus couramment utilisé max(vc8)
au lieu de ORDER BY
/ LIMIT 1
, car les max()
amis se contentent text
immédiatement (ou du type de base respectif sans modificateur).
SQL Fiddle démontrant 3 choses:
- Une gamme d'exemples d'expressions incluant des résultats surprenants.
- Un rCTE simple qui fonctionne avec
varchar
(sans modificateur). - Le même rCTE levant une exception pour
varchar(n)
(avec modificateur).
Le violon est pour pg 9.3. J'obtiens les mêmes résultats localement pour la page 9.4.4.
J'ai créé des tableaux à partir des expressions de démonstration pour pouvoir montrer le type de données exact, y compris le modificateur. Bien que pgAdmin affiche ces informations hors de la boîte, elles ne sont pas disponibles auprès de sqlfiddle. Remarquablement, il n'est pas non plus disponible en psql
(!). C'est une lacune connue de psql et une solution possible a déjà été discutée sur pgsql-hackers - mais pas encore implémentée. Cela pourrait être l'une des raisons pour lesquelles le problème n'a pas encore été détecté et résolu.
Au niveau SQL, vous pouvez utiliser pg_typeof()
pour obtenir le type (mais pas le modificateur).
Des questions
Ensemble, les 3 problèmes font le bordel.
Pour être précis, le problème 1. n'est pas directement impliqué, mais il ruine la correction apparemment évidente avec un plâtre au terme non récursif: ARRAY[vc8]::varchar[]
ou similaire, ce qui ajoute à la confusion.
Lequel de ces éléments est un bug, un problème ou tout simplement comment il est censé être?
Suis-je en train de manquer quelque chose ou devons-nous signaler un bug?
UNION
requêtes simples . Se pourrait-il que nous ayons trouvé trois petits bogues indépendants à la fois? (Après des mois et des mois sans une telle découverte.) Lequel de ceux qui, selon vous, devrait être classé comme bug?Réponses:
Cela est dû aux attributs de relation (définis dans
pg_class
etpg_attribute
, ou définis dynamiquement à partir d'uneselect
instruction) prenant en charge les modificateurs (viapg_attribute.atttypmod
), contrairement aux paramètres de fonction . Les modificateurs sont perdus lorsqu'ils sont traités via des fonctions, et puisque tous les opérateurs sont gérés via des fonctions, les modificateurs sont également perdus lorsqu'ils sont traités par des opérateurs.Les fonctions avec des valeurs de sortie, ou qui renvoient des ensembles d'enregistrements, ou l'équivalent,
returns table(...)
ne peuvent pas non plus conserver les modificateurs inclus dans la définition. Cependant, les tables quireturn setof <type>
conserveront (en fait, probablement transtypées en) tous les modificateurs définis pourtype
danspg_attribute
.la source