web-dev-qa-db-de.com

Umgang mit Unicode-Sequenzen in Postgresql

Ich habe einige JSON-Daten in einer JSON-Spalte (nicht JSONB) in meiner Postgresql-Datenbank (9.4.1) gespeichert. Einige dieser JSON-Strukturen enthalten in ihren Attributwerten Unicode-Sequenzen. Zum Beispiel:

{"client_id": 1, "device_name": "FooBar\ufffd\u0000\ufffd\u000f\ufffd" }

Wenn ich versuche, diese JSON-Spalte abzufragen (auch wenn ich nicht direkt auf das device_name-Attribut zugreifen möchte), wird die folgende Fehlermeldung angezeigt:

ERROR: nicht unterstützte Unicode-Escape-Sequenz
Detail: \u0000 kann nicht in Text umgewandelt werden.

Sie können diesen Fehler erneut erstellen, indem Sie den folgenden Befehl auf einem Postgresql-Server ausführen:

select '{"client_id": 1, "device_name": "FooBar\ufffd\u0000\ufffd\u000f\ufffd" }'::json->>'client_id'

Der Fehler ist für mich sinnvoll - es gibt einfach keine Möglichkeit, die Unicode-Sequenz NULL in einem Textergebnis darzustellen.

Gibt es eine Möglichkeit, die gleichen JSON-Daten abzufragen, ohne die eingehenden Daten "sanieren" zu müssen? Diese JSON-Strukturen werden regelmäßig geändert, sodass das Durchsuchen eines bestimmten Attributs (in diesem Fall device_name) keine gute Lösung darstellt, da andere Attribute leicht vorhanden sein könnten, die ähnliche Daten enthalten könnten.


Nach einigen weiteren Untersuchungen scheint dieses Verhalten für die Version 9.4.1 neu zu sein, wie im Changelog erwähnt :

... Daher wird \u0000 jetzt auch in json-Werten abgelehnt, wenn eine Konvertierung in eine entsperrte Form erforderlich ist. Diese Änderung beeinträchtigt nicht die Möglichkeit, \u0000 in json-Spalten zu speichern, solange die Werte nicht verarbeitet werden.

War das wirklich die Absicht? Ist ein Downgrade auf 9.4.1 eine sinnvolle Option?


Als Nebenbemerkung wird diese Eigenschaft vom Namen des mobilen Geräts des Clients übernommen - es ist der Benutzer, der diesen Text in das Gerät eingegeben hat. Wie in aller Welt hat ein Benutzer NULL und REPLACEMENT CHARACTER Werte eingefügt?!

26
Lix

\u0000 ist der einzige Unicode-Codepunkt, der in einer Zeichenfolge nicht gültig ist. Ich sehe keine andere Möglichkeit, als die Saite zu desinfizieren.

Da json nur eine Zeichenfolge in einem bestimmten Format ist, können Sie die Standardzeichenfolgenfunktionen verwenden, ohne sich um die JSON-Struktur kümmern zu müssen. Ein einzeiliges Desinfektionsmittel zum Entfernen des Codepunkts wäre:

SELECT (regexp_replace(the_string::text, '\\u0000', '', 'g'))::json;

Sie können aber auch beliebige Zeichen einfügen, die nützlich sind, wenn der Nullcodepunkt als Trennzeichen verwendet wird.

Beachten Sie auch den geringfügigen Unterschied zwischen dem, was in der Datenbank gespeichert ist und wie es dem Benutzer präsentiert wird. Sie können den Codepunkt in einer JSON-Zeichenfolge speichern, müssen jedoch vor der Verarbeitung des Werts als json-Datentyp ein anderes Zeichen vorverarbeiten.

23
Patrick

Die Lösung von Patrick hat für mich nicht funktioniert. Trotzdem wurde immer ein Fehler geworfen. Ich recherchierte ein wenig weiter und konnte eine kleine benutzerdefinierte Funktion schreiben, die das Problem für mich korrigierte.

Zuerst konnte ich den Fehler reproduzieren, indem ich schrieb:

select json '{ "a":  "null \u0000 escape" }' ->> 'a' as fails

Dann habe ich eine benutzerdefinierte Funktion hinzugefügt, die ich in meiner Abfrage verwendet habe:

CREATE OR REPLACE FUNCTION null_if_invalid_string(json_input JSON, record_id UUID)
  RETURNS JSON AS $$
DECLARE json_value JSON DEFAULT NULL;
BEGIN
  BEGIN
    json_value := json_input ->> 'location';
    EXCEPTION WHEN OTHERS
    THEN
      RAISE NOTICE 'Invalid json value: "%".  Returning NULL.', record_id;
      RETURN NULL;
  END;
  RETURN json_input;
END;
$$ LANGUAGE plpgsql;

So rufen Sie die Funktion auf. Sie sollten keine Fehlermeldung erhalten.

select null_if_invalid_string('{ "a":  "null \u0000 escape" }', id) from my_table

Während dies den Json wie erwartet zurückgeben sollte:

select null_if_invalid_string('{ "a":  "null" }', id) from my_table
0
Hendrik