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 varchar
colonna 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 UNION
query 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 text
subito (o il rispettivo tipo di base senza modificatore).
SQL Fiddle dimostrando 3 cose:
- Una serie di espressioni di esempio che includono risultati sorprendenti.
- Un semplice rCTE che funziona con
varchar
(senza modificatore). - 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?
UNION
query. 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?