Bei der Diskussion einer rekursiven CTE-Lösung für diese Frage:
@ypercube stieß auf eine überraschende Ausnahme, die uns veranlasste, den Umgang mit Typmodifikatoren zu untersuchen. Wir fanden überraschendes Verhalten.
1. Type Cast behält in einigen Kontexten den Typmodifikator bei
Auch wenn nicht angewiesen. Das grundlegendste Beispiel:
SELECT 'vc8'::varchar(8)::varchar
Man könnte erwarten varchar
(kein Modifikator), zumindest würde ich. Aber das Ergebnis ist varchar(8)
(mit Modifikator). Viele verwandte Fälle in der Geige unten.
2. Die Array-Verkettung verliert in einigen Kontexten den Typmodifikator
Ohne Notwendigkeit, also irrt dies auf der anderen Seite:
SELECT ARRAY['vc8']::varchar(8)[]
, ARRAY['vc8']::varchar(8)[] || 'vc8'::varchar(8)
Der 1. Ausdruck ergibt varchar(8)[]
wie erwartet.
Aber die zweite, nach der Verkettung einer anderen, varchar(8)
wird auf nur varchar[]
(kein Modifikator) verwässert . Ähnliches Verhalten aus array_append()
Beispielen in der folgenden Geige.
All dies spielt in den meisten Kontexten keine Rolle. Postgres verliert keine Daten, und wenn es einer Spalte zugewiesen wird, wird der Wert ohnehin auf den richtigen Typ gezwungen. Jedoch , irrend in einer überraschenden Ausnahme in entgegengesetzten Richtungen gipfeln:
3. Rekursiver CTE verlangt, dass die Datentypen genau übereinstimmen
Angesichts dieser vereinfachten Tabelle:
CREATE TABLE a (
vc8 varchar(8) -- with modifier
, vc varchar -- without
);
INSERT INTO a VALUES ('a', 'a'), ('bb', 'bb');
Während dieser rCTE für die varchar
Spalte funktioniert vc
, schlägt er für die varchar(8)
Spalte fehl 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;
FEHLER: Bei der rekursiven Abfrage "cte" in Spalte 1 variiert das Typzeichen (8) [] nicht rekursiv, aber das Typzeichen variiert [] insgesamt Hinweis: Wandeln Sie die Ausgabe des nicht rekursiven Terms in den richtigen Typ um. Position: 103
Eine schnelle Problemumgehung wäre das Casting text
.
Eine einfache UNION
Abfrage weist nicht das gleiche Problem auf: Sie entscheidet sich für den Typ ohne Modifikator, wodurch garantiert alle Informationen erhalten bleiben. Aber der rCTE ist wählerischer.
Außerdem würden Sie nicht auf Probleme mit dem häufiger verwendeten max(vc8)
anstelle von ORDER BY
/ stoßen LIMIT 1
, da sich max()
Freunde text
sofort damit zufrieden geben (oder mit dem jeweiligen Basistyp ohne Modifikator).
SQL Fiddle demonstriert 3 Dinge:
- Eine Reihe von Beispielausdrücken mit überraschenden Ergebnissen.
- Ein einfacher rCTE, der mit
varchar
(ohne Modifikator) funktioniert . - Der gleiche rCTE löst eine Ausnahme für
varchar(n)
(mit Modifikator) aus.
Die Geige ist für S. 9.3. Ich erhalte die gleichen Ergebnisse lokal für S. 9.4.4.
Ich habe Tabellen aus den Demo-Ausdrücken erstellt, um den genauen Datentyp einschließlich des Modifikators anzeigen zu können. Während pgAdmin diese Informationen sofort anzeigt, sind sie bei sqlfiddle nicht verfügbar. Bemerkenswerterweise ist es auch nicht in psql
(!) Verfügbar . Dies ist ein bekanntes Manko in psql, und eine mögliche Lösung wurde bereits bei pgsql-Hackern diskutiert - aber noch nicht implementiert. Dies könnte einer der Gründe sein, warum das Problem noch nicht erkannt und behoben wurde.
Auf SQL-Ebene können Sie pg_typeof()
den Typ (aber nicht den Modifikator) abrufen.
Fragen
Zusammen machen die 3 Probleme ein Chaos.
Um genau zu sein, ist Problem 1. nicht direkt involviert, aber es ruiniert die scheinbar offensichtliche Lösung mit einer Besetzung im nicht rekursiven Begriff: ARRAY[vc8]::varchar[]
oder ähnlich, was die Verwirrung erhöht.
Welches dieser Elemente ist ein Fehler, eine Panne oder wie es sein soll?
Vermisse ich etwas oder sollten wir einen Fehler melden?
UNION
Abfragen. Könnte es sein, dass wir drei unabhängige kleine Fehler gleichzeitig gefunden haben? (Nach Monaten und Monaten ohne solchen Fund.) Welche davon sollten Ihrer Meinung nach als Fehler gemeldet werden?Antworten:
Dies liegt an Beziehungsattributen (definiert in
pg_class
undpg_attribute
oder dynamisch aus einerselect
Anweisung definiert), die Modifikatoren (viapg_attribute.atttypmod
) unterstützen, während Funktionsparameter dies nicht tun. Modifikatoren gehen verloren, wenn sie über Funktionen verarbeitet werden, und da alle Operatoren über Funktionen behandelt werden, gehen Modifikatoren auch verloren, wenn sie von Operatoren verarbeitet werden.Funktionen mit Ausgabewerten oder die Rückgabe von Datensatzsätzen oder der entsprechenden Werte können
returns table(...)
auch keine in der Definition enthaltenen Modifikatoren beibehalten. Allerdings Tabellen , diereturn setof <type>
beibehalten werden (tatsächlich wahrscheinlich Typumwandlung zu) alle Modifikatoren definierttype
inpg_attribute
.quelle