Mientras discute una solución recursiva de CTE para esta pregunta:
@ypercube se topó con una excepción sorprendente, que nos llevó a investigar el manejo de modificadores de tipo. Encontramos un comportamiento sorprendente.
1. La conversión de tipos conserva el modificador de tipo en algunos contextos.
Incluso cuando se le indique que no lo haga. El ejemplo más básico:
SELECT 'vc8'::varchar(8)::varchar
Uno podría esperar varchar
(sin modificador), al menos lo haría. Pero el resultado es varchar(8)
(con modificador). Muchos casos relacionados en el violín a continuación.
2. La concatenación de matriz pierde el modificador de tipo en algunos contextos
Sin necesidad, esto se equivoca en el lado opuesto:
SELECT ARRAY['vc8']::varchar(8)[]
, ARRAY['vc8']::varchar(8)[] || 'vc8'::varchar(8)
La primera expresión produce varchar(8)[]
como se esperaba.
Pero el segundo, después de concatenar a otro, varchar(8)
se diluye a solo varchar[]
(sin modificador). Comportamiento similar de array_append()
, ejemplos en el violín a continuación.
Todo esto no importa en la mayoría de los contextos. Postgres no pierde datos, y cuando se asigna a una columna, el valor se coacciona al tipo correcto de todos modos. Sin embargo , errar en direcciones opuestas culmina en una sorprendente excepción:
3. El CTE recursivo exige que los tipos de datos coincidan exactamente
Dada esta tabla simplificada:
CREATE TABLE a (
vc8 varchar(8) -- with modifier
, vc varchar -- without
);
INSERT INTO a VALUES ('a', 'a'), ('bb', 'bb');
Si bien este rCTE funciona para la varchar
columna vc
, falla para la varchar(8)
columna 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;
ERROR: la consulta recursiva "cte" columna 1 tiene caracteres de tipo variable (8) [] en términos no recursivos pero caracteres de tipo variables [] en general Sugerencia: Transmita la salida del término no recursivo al tipo correcto. Puesto: 103
Una solución rápida sería lanzar a text
.
Una UNION
consulta simple no presenta el mismo problema: se conforma con el tipo sin modificador, lo que garantiza preservar toda la información. Pero el rCTE es más exigente.
Además, no tendría problemas con los más utilizados en max(vc8)
lugar de ORDER BY
/ LIMIT 1
, porque los max()
amigos se conforman de text
inmediato (o el tipo base respectivo sin modificador).
SQL Fiddle que demuestra 3 cosas:
- Una gama de expresiones de ejemplo que incluyen resultados sorprendentes.
- Un rCTE simple que funciona con
varchar
(sin modificador). - El mismo rCTE provoca una excepción para
varchar(n)
(con modificador).
El violín es para la página 9.3. Obtengo los mismos resultados localmente para la página 9.4.4.
Creé tablas a partir de las expresiones de demostración para poder mostrar el tipo de datos exacto, incluido el modificador. Si bien pgAdmin muestra esta información de fábrica, no está disponible desde sqlfiddle. Sorprendentemente, tampoco está disponible en psql
(!). Esta es una deficiencia conocida en psql y se ha discutido una posible solución en pgsql-hackers antes , pero aún no se ha implementado. Esta podría ser una de las razones por las que el problema aún no se ha detectado y solucionado.
En el nivel SQL, puede usar pg_typeof()
para obtener el tipo (pero no el modificador).
Preguntas
Juntos, los 3 problemas hacen un desastre.
Para ser precisos, el problema 1. no está involucrado directamente, pero arruina la solución aparentemente obvia con un reparto en el término no recursivo: ARRAY[vc8]::varchar[]
o similar, lo que se suma a la confusión.
¿Cuál de estos elementos es un error, una falla o simplemente cómo se supone que debe ser?
¿Me estoy perdiendo algo o debemos informar un error?
UNION
consultas simples . ¿Podría ser que encontramos tres pequeños errores independientes a la vez? (Después de meses y meses sin tal hallazgo). ¿Cuál de ellos sentiría que debería presentarse como error?Respuestas:
Esto se debe a que los atributos de relación (definidos en
pg_class
ypg_attribute
, o definidos dinámicamente a partir de unaselect
declaración) que admiten modificadores (víapg_attribute.atttypmod
), mientras que los parámetros de función no. Los modificadores se pierden cuando se procesan a través de funciones, y dado que todos los operadores se manejan a través de funciones, los modificadores se pierden cuando los operadores también los procesan.Las funciones con valores de salida, o que devuelven conjuntos de registros, o su equivalente
returns table(...)
, tampoco pueden retener ningún modificador incluido en la definición. Sin embargo, las tablas quereturn setof <type>
retendrán (en realidad, probablemente encastrarán) cualquier modificador definido paratype
inpg_attribute
.fuente