Risultati sorprendenti per tipi di dati con modificatore di tipo


11

Durante la discussione di una soluzione CTE ricorsiva per questa domanda:

@ypercube si è imbattuto in un'eccezione sorprendente, che ci ha portato a studiare la gestione dei modificatori di tipo. Abbiamo trovato un comportamento sorprendente.

1. Type cast mantiene il modificatore di tipo in alcuni contesti

Anche quando viene richiesto di non farlo. L'esempio più semplice:

SELECT 'vc8'::varchar(8)::varchar

Ci si potrebbe aspettare varchar(nessun modificatore), almeno lo farei. Ma il risultato è varchar(8)(con modificatore). Molti casi correlati nel violino qui sotto.

2. La concatenazione di array perde il modificatore di tipo in alcuni contesti

Senza necessità, quindi questo errore sul lato opposto:

SELECT ARRAY['vc8']::varchar(8)[]
     , ARRAY['vc8']::varchar(8)[] || 'vc8'::varchar(8)

La prima espressione produce varchar(8)[]come previsto.
Ma il 2 °, dopo aver concatenato un altro, varchar(8)viene annacquato a solo varchar[](nessun modificatore). Comportamento simile da array_append(), esempi nel violino in basso.

Tutto ciò non ha importanza nella maggior parte dei contesti. Postgres non perde i dati e, se assegnato a una colonna, il valore viene comunque forzato nel tipo giusto. Tuttavia , errare in direzioni opposte culmina in un'eccezione sorprendente:

3. Il CTE ricorsivo richiede che i tipi di dati corrispondano esattamente

Data questa tabella semplificata:

CREATE TABLE a (
  vc8  varchar(8)  -- with modifier
, vc   varchar     -- without  
);
INSERT INTO a VALUES  ('a',  'a'), ('bb', 'bb');

Mentre questo rCTE funziona per la varcharcolonna vc, non riesce per la varchar(8)colonna 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;
ERRORE: la query ricorsiva "cte" nella colonna 1 presenta caratteri di tipo variabile (8) [] in termini non ricorsivi ma caratteri di carattere che variano [] in generale  
Suggerimento: eseguire il cast dell'output del termine non ricorsivo sul tipo corretto. Posizione: 103

Una soluzione alternativa sarebbe lanciare text.

Una UNIONquery semplice non presenta lo stesso problema: si accontenta del tipo senza modificatore, che è garantito per preservare tutte le informazioni. Ma l'rCTE è più esigente.

Inoltre, non incontreresti problemi con il più comunemente usato max(vc8)invece di ORDER BY/ LIMIT 1, perché max()e gli amici si accontentano textsubito (o il rispettivo tipo di base senza modificatore).

SQL Fiddle dimostrando 3 cose:

  1. Una serie di espressioni di esempio che includono risultati sorprendenti.
  2. Un semplice rCTE che funziona con varchar(senza modificatore).
  3. Lo stesso rCTE solleva un'eccezione per varchar(n)(con modificatore).

Il violino è per pag 9.3. Ottengo localmente gli stessi risultati per pag. 9.4.4.

Ho creato tabelle dalle espressioni demo per essere in grado di mostrare il tipo esatto di dati incluso il modificatore. Mentre pgAdmin mostra queste informazioni per impostazione predefinita, non sono disponibili da sqlfiddle. Sorprendentemente, non è disponibile anche in psql(!). Questo è noto difetto in psql e una possibile soluzione è stata discussa in precedenza su pgsql-hacker , ma non ancora implementata. Questo potrebbe essere uno dei motivi per cui il problema non è stato ancora rilevato e risolto.

A livello SQL, è possibile utilizzare pg_typeof()per ottenere il tipo (ma non il modificatore).

Domande

Insieme, i 3 numeri fanno un casino.
Per essere precisi, il problema 1. non è direttamente coinvolto, ma rovina la correzione apparentemente ovvia con un cast nel termine non ricorsivo: ARRAY[vc8]::varchar[]o simile, che aggiunge confusione.
Quale di questi elementi è un bug, un problema tecnico o come dovrebbe essere?
Mi sto perdendo qualcosa o dovremmo segnalare un bug?


Questo sembra certamente piuttosto sospetto. Sospetto che la compatibilità con le versioni precedenti delle query sindacali esistenti possa avere un ruolo.
Craig Ringer,

@CraigRinger: non vedo perché la concatenazione dell'array lasci il modificatore senza necessità e il cast no, anche se richiesto. Né il motivo per cui rCTE deve essere più rigoroso (meno intelligente) delle semplici UNIONquery. Potrebbe essere che abbiamo trovato tre piccoli bug indipendenti contemporaneamente? (Dopo mesi e mesi in cui non è stato trovato.) Quale di quelli che ritieni debbano essere archiviati come bug?
Erwin Brandstetter,

Risposte:


1

Ciò è dovuto agli attributi di relazione (definiti in pg_classe pg_attribute, o definiti dinamicamente da selectun'istruzione) che supportano i modificatori (via pg_attribute.atttypmod), mentre i parametri della funzione no. I modificatori vengono persi quando vengono elaborati tramite le funzioni e poiché tutti gli operatori vengono gestiti tramite le funzioni, i modificatori vengono persi anche quando vengono elaborati dagli operatori.

Le funzioni con valori di output o che restituiscono set di record o equivalenti non returns table(...)sono inoltre in grado di conservare i modificatori inclusi nella definizione. Tuttavia, le tabelle che return setof <type>manterranno (effettivamente, probabilmente il typecast in) qualsiasi modificatore definito per typein pg_attribute.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.