Welche Auswirkungen hat LC_CTYPE auf eine PostgreSQL-Datenbank?


25

Also habe ich einige Debian-Server mit PostgreSQL drauf. In der Vergangenheit wurden diese Server und PostgreSQL mit dem lateinischen Zeichensatz 9 lokalisiert, und damals war es in Ordnung. Jetzt müssen wir uns mit Dingen wie Polnisch, Griechisch oder Chinesisch befassen, damit das Ändern zu einem wachsenden Problem wird.

Als ich versuchte, eine UTF8-Datenbank zu erstellen, wurde folgende Meldung angezeigt:

FEHLER: Codierung UTF8 stimmt nicht mit der Ländereinstellung fr_FR überein. Detail: Für die ausgewählte Einstellung LC_CTYPE ist die Codierung LATIN9 erforderlich.

Einige Male habe ich mit meinem alten Freund Google zu diesem Thema recherchiert, und alles, was ich finden konnte, waren einige überkomplizierte Prozeduren wie das Aktualisieren von Debian LANG, das Neukompilieren von PostgreSQL mit dem richtigen Zeichensatz, das Bearbeiten aller LC_Systemvariablen und andere obskure Lösungen. Daher lassen wir dieses Problem vorerst beiseite.

Kürzlich kam es wieder zurück, die Griechen wollen das Zeug und Latin 9 will es nicht. Und während ich mich erneut mit diesem Thema beschäftigte, kam ein Kollege auf mich zu und sagte: "Nein, es ist einfach, sieh mal."

Er hat nichts editiert, hat keine Zaubertricks gemacht, er hat nur diese SQL-Abfrage gemacht:

CREATE DATABASE my_utf8_db
  WITH ENCODING='UTF8'
       OWNER=admin
       TEMPLATE=template0
       LC_COLLATE='C'
       LC_CTYPE='C'
       CONNECTION LIMIT=-1
       TABLESPACE=pg_default;

Und es hat gut funktioniert.

Ich wusste eigentlich nichts davon LC_CTYPE='C'und war überrascht, dass dies nicht bei den ersten Lösungen bei Google und sogar bei Stack Overflow der Fall war. Ich habe mich umgesehen und nur in der PostgreSQL-Dokumentation eine Erwähnung gefunden.

Wenn LC_CTYPE C oder POSIX ist, ist jeder Zeichensatz zulässig, aber für andere Einstellungen von LC_CTYPE gibt es nur einen Zeichensatz, der ordnungsgemäß funktioniert. Da die LC_CTYPE-Einstellung von initdb eingefroren wird, ist die offensichtliche Flexibilität, verschiedene Codierungen in verschiedenen Datenbanken eines Clusters zu verwenden, eher theoretisch als real, es sei denn, Sie wählen das Gebietsschema C oder POSIX (wodurch die reale Gebietsschemabe deaktiviert wird).

Ich habe mich gefragt, ob das zu einfach und zu perfekt ist. Was sind die Nachteile? Und es fällt mir schwer, eine Antwort zu finden. Also hier komme ich hier posten:

tl; dr: Was sind die Nachteile LC_CTYPE='C'einer bestimmten Lokalisierung? Ist es schlimm das zu tun? Was soll ich erwarten, um zu brechen?

Antworten:


25

Was sind die Nachteile der Verwendung von LC_CTYPE = 'C' über eine bestimmte Lokalisierung

In der Dokumentation wird die Beziehung zwischen Gebietsschemas und SQL-Funktionen in der Gebietsschema-Unterstützung erwähnt :

Die Gebietsschemaeinstellungen wirken sich auf die folgenden SQL-Funktionen aus:

  • Sortierreihenfolge in Abfragen mit ORDER BY oder den Standardvergleichsoperatoren für Textdaten

  • Die Funktionen upper, lower und initcap

  • Mustervergleichsoperatoren (reguläre Ausdrücke im LIKE-, SIMILAR TO- und POSIX-Stil); Gebietsschemata wirken sich sowohl auf die Übereinstimmung ohne Berücksichtigung der Groß- und Kleinschreibung als auch auf die Klassifizierung von Zeichen durch reguläre Ausdrücke der Zeichenklasse aus

  • Die to_char-Funktionsfamilie

  • Die Möglichkeit, Indizes mit LIKE-Klauseln zu verwenden

Das erste Element (Sortierreihenfolge) handelt von LC_COLLATEund die anderen scheinen alle davon zu handeln LC_CTYPE.

LC_COLLATE

LC_COLLATEwirkt sich auf Vergleiche zwischen Zeichenfolgen aus. In der Praxis ist die Sortierreihenfolge der sichtbarste Effekt. LC_COLLATE='C'(oder POSIXwas ein Synonym ist) bedeutet, dass es die Bytereihenfolge ist, die Vergleiche steuert, während ein Gebietsschema in der language_REGIONForm bedeutet, dass kulturelle Regeln die Vergleiche steuern.

Ein Beispiel mit französischen Namen, ausgeführt aus einer UTF-8-Datenbank:

select firstname from (values ('bernard'), ('bérénice'), ('béatrice'), ('boris'))
 AS l(firstname)
order by firstname collate "fr_FR";

Ergebnis:

 Vorname 
-----------
 béatrice
 bérénice
 bernard
 boris

béatricekommt vor boris, weil das akzentuierte E mit O verglichen wird, als wäre es nicht akzentuiert. Es ist eine kulturelle Regel.

Dies unterscheidet sich von dem, was mit einem CGebietsschema passiert :

select firstname from (values ('bernard'), ('bérénice'), ('béatrice'), ('boris')) 
 AS l(firstname)
order by firstname collate "C";

Ergebnis:

 Vorname 
-----------
 bernard
 boris
 béatrice
 bérénice

Nun werden die Namen mit dem Akzent E am Ende der Liste verschoben. Die Bytedarstellung von éin UTF-8 ist das Hexadezimal C3 A9und für oes 6f. c3ist größer als 6fso unter dem CGebietsschema 'béatrice' > 'boris'.

Es sind nicht nur Akzente. Es gibt komplexere Regeln mit Silbentrennung, Interpunktion und seltsamen Zeichen wie œ. Seltsame kulturelle Regeln sind in jedem Gebietsschema zu erwarten.

Wenn nun die zu vergleichenden Zeichenfolgen verschiedene Sprachen mischen, beispielsweise wenn eine firstnameSpalte für Personen aus aller Welt vorhanden ist, sollte ein bestimmtes Gebietsschema ohnehin nicht dominieren, da unterschiedliche Alphabete für verschiedene Sprachen nicht so konzipiert wurden gegeneinander sortiert.

In diesem Fall Cist es eine rationale Wahl, und sie hat den Vorteil, schneller zu sein, weil nichts reinen Byte-Vergleichen standhalten kann.

LC_CTYPE

Auf LC_CTYPE'C' gesetzt zu haben, bedeutet, dass C nur für Zeichen im US-ASCII-Bereich (dh bis zum Codepunkt 0x7F in Unicode) wie C funktioniert isupper(c)oder tolower(c)erwartete Ergebnisse liefert.

Da SQL - Funktionen wie upper(), lower()oder initcap sind in Postgres auf diesen libc - Funktionen implementiert, sind sie davon betroffen, sobald es nicht US-ASCII - Zeichen in Strings.

Beispiel:

test=> show lc_ctype;
  lc_ctype   
-------------
 fr_FR.UTF-8
(1 row)

-- Good result
test=> select initcap('élysée');
 initcap 
---------
 Élysée
(1 row)

-- Wrong result
-- collate "C" is the same as if the db has been created with lc_ctype='C'
test=> select initcap('élysée' collate "C");
 initcap 
---------
 éLyséE
(1 row)

Wird für das CGebietsschema éals nicht kategorisierbares Zeichen behandelt.

Ähnlich falsche Ergebnisse werden auch mit regulären Ausdrücken erhalten:

test=> select 'élysée' ~ '^\w+$';
 ?column? 
----------
 t
(1 row)

test=> select 'élysée' COLLATE "C" ~ '^\w+$';
 ?column? 
----------
 f
(1 row)

Wenn ich es richtig verstehe, hätten wir das Problem mit der Bestellung, selbst wenn Sie einen UTF-8-Server erstellt hätten? Ich denke, wenn das System LC_CTYPE auf UTF-8 gesetzt ist oder PostgreSQL in UTF-8 kompiliert wird, ergibt sich das gleiche Vergleichsproblem wie bei Ihnen.
Gregoire D.

Wäre es möglich, das Zusammentragen von Abfragen zu erzwingen, damit der Vergleich lokal korrekt ist?
Gregoire D.

Ja, einzelne Zeichenkettenvergleiche können ihre eigenen Kollatierungsregeln einbetten, wie ich es in dieser Antwort mit dem collate "C"nach dem mache order by. Sie bestimmen, ob und wo Ihre Anwendung dies benötigt. Die meisten Anwendungen interessieren sich nicht wirklich dafür.
Daniel Vérité

1
Beachten Sie auch, dass einzelne Spalten möglicherweise einen COLLATEBezeichner haben, der sich von dem der Datenbank unterscheidet.
Daniel Vérité

2
Diese Antwort ist wirklich für LC_COLLATE, nicht für LC_CTYPE. LC_CTYPE wird verwendet, um zu entscheiden, ob ein Zeichen eine Ziffer, ein Buchstabe, ein Leerzeichen, eine Interpunktion usw. ist
jjanes

10

Beachten Sie in Bezug auf Daniels akzeptierte Antwort zum Sortieren anhand von Kollatierungen, dass PostgreSQL auf einem Mac möglicherweise nicht wie erwartet funktioniert, da für einige Kollatierungen auf Betriebssystemebene keine ausreichenden Einstellungen vorgenommen wurden. Sie können hier mehr über das Problem lesen:

http://www.postgresql.org/message-id/4B4E845F.80906@postnewspapers.com.au

Dies ist kein spezielles PostgreSQL-Problem, sondern ein Problem mit der Standardkonfiguration von Mac für Sortierungseinstellungen. Auf meinem aktuellen System wird PostgreSQL 9.3 unter OS X El Capitan Version 10.11 ausgeführt und dieses Problem tritt auf. Mein System gibt die gleichen Abfrageergebnisse zurück, unabhängig davon, ob ich die Kollatierung "fr_FR" oder "en_US" verwende. Beispielsweise:

Verwenden der Kollatierung "fr_FR":

select firstname from (values ('bernard'), ('bérénice'), ('béatrice'), ('boris'))
AS l(firstname)
order by firstname collate "fr_FR";

results:
==============
bernard
boris
béatrice
bérénice

Verwenden der Kollatierung "en_US":

select firstname from (values ('bernard'), ('bérénice'), ('béatrice'), ('boris'))
AS l(firstname)
order by firstname collate "en_US";

results:
==============
bernard
boris
béatrice
bérénice

Auf meinem System sind die Sortierungseinstellungen (auf Betriebssystemebene) für "fr_FR" und "en_US" dieselben, wie in der Shell durch Ausführen von diff gezeigt:

cd /usr/share/locale
diff fr_FR.UTF-8/LC_COLLATE en_US.UTF-8/LC_COLLATE

Hoffentlich sind diese zusätzlichen Informationen hilfreich für alle, die dies lesen und PostgreSQL auf einem Mac verwenden, der unter diesem Problem leidet.


Wie kann ich es auf modernen Macs zum Laufen bringen? Haben Sie irgendetwas durchgemacht, damit es auf Ihrem Mac funktioniert?
Dinesh Kumar
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.