Überraschende Ergebnisse für Datentypen mit Typmodifikator

11

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 varcharSpalte 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 UNIONAbfrage 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 textsofort damit zufrieden geben (oder mit dem jeweiligen Basistyp ohne Modifikator).

SQL Fiddle demonstriert 3 Dinge:

  1. Eine Reihe von Beispielausdrücken mit überraschenden Ergebnissen.
  2. Ein einfacher rCTE, der mit varchar(ohne Modifikator) funktioniert .
  3. 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?

Erwin Brandstetter
quelle
Dies scheint sicherlich eher verdächtig. Ich vermute, dass die Abwärtskompatibilität für vorhandene Gewerkschaftsabfragen eine Rolle spielt.
Craig Ringer
@CraigRinger: Ich verstehe nicht, warum die Array-Verkettung den Modifikator ohne Notwendigkeit löscht und die Besetzung nicht, obwohl angefordert. Weder warum der rCTE strenger (weniger klug) sein muss als einfache UNIONAbfragen. 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?
Erwin Brandstetter

Antworten:

1

Dies liegt an Beziehungsattributen (definiert in pg_classund pg_attributeoder dynamisch aus einer selectAnweisung definiert), die Modifikatoren (via pg_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 , die return setof <type>beibehalten werden (tatsächlich wahrscheinlich Typumwandlung zu) alle Modifikatoren definiert typein pg_attribute.

Ziggy Crueltyfree Zeitgeister
quelle