Resultados sorprendentes para tipos de datos con modificador de tipo


11

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 varcharcolumna 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 UNIONconsulta 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 textinmediato (o el tipo base respectivo sin modificador).

SQL Fiddle que demuestra 3 cosas:

  1. Una gama de expresiones de ejemplo que incluyen resultados sorprendentes.
  2. Un rCTE simple que funciona con varchar(sin modificador).
  3. 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?


Esto ciertamente parece bastante sospechoso. Sospecho que la compatibilidad con versiones anteriores para consultas sindicales existentes puede ser importante.
Craig Ringer

@CraigRinger: No veo por qué la concatenación de matriz deja caer el modificador sin necesidad y la conversión no lo hace, aunque se solicite. Tampoco por qué el rCTE tiene que ser más estricto (menos inteligente) que las UNIONconsultas 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?
Erwin Brandstetter

Respuestas:


1

Esto se debe a que los atributos de relación (definidos en pg_classy pg_attribute, o definidos dinámicamente a partir de una selectdeclaración) que admiten modificadores (vía pg_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 que return setof <type>retendrán (en realidad, probablemente encastrarán) cualquier modificador definido para typein pg_attribute.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.