Jaki jest wpływ LC_CTYPE na bazę danych PostgreSQL?

25

Mam więc kilka serwerów Debiana z PostgreSQL. Historycznie te serwery i PostgreSQL były zlokalizowane przy użyciu zestawu znaków Latin 9, a wtedy było dobrze. Teraz musimy poradzić sobie z takimi sprawami, jak polski, grecki czy chiński, więc zmiana staje się coraz większym problemem.

Kiedy próbowałem utworzyć bazę danych UTF8, otrzymałem komunikat:

BŁĄD: kodowanie UTF8 nie pasuje do ustawień narodowych fr_FR Szczegół: Wybrane ustawienie LC_CTYPE wymaga kodowania LATIN9.

Kilka razy przeprowadzałem badania na ten temat z moim starym kumplem Google, a wszystko, co mogłem znaleźć, to zbyt skomplikowane procedury, takie jak aktualizacja Debiana LANG, rekompilacja PostgreSQL z poprawnym zestawem znaków, edycja wszystkich LC_zmiennych systemowych i innych niejasnych rozwiązań. Na razie pomijamy ten problem.

Niedawno wróciło, Grecy chcą tego, a Latin 9 nie chce. I kiedy ponownie analizowałem ten problem, jeden ze współpracowników podszedł do mnie i powiedział: „Nie, to proste, patrz”.

Nic nie edytował, nie robił magicznych sztuczek, po prostu wykonał to zapytanie SQL:

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

I działało dobrze.

Właściwie nie wiedziałem o tym LC_CTYPE='C'i byłem zaskoczony, że tego nie wykorzystałem w pierwszych rozwiązaniach w Google, a nawet w Stack Overflow. Rozejrzałem się i znalazłem tylko wzmiankę w dokumentacji PostgreSQL.

Gdy LC_CTYPE to C lub POSIX, dowolny zestaw znaków jest dozwolony, ale w przypadku innych ustawień LC_CTYPE istnieje tylko jeden zestaw znaków, który będzie działał poprawnie. Ponieważ ustawienie LC_CTYPE jest zamrożone przez initdb, pozorna elastyczność w stosowaniu różnych kodowań w różnych bazach danych klastra jest bardziej teoretyczna niż rzeczywista, z wyjątkiem sytuacji, gdy wybierzesz ustawienia regionalne C lub POSIX (wyłączając w ten sposób rozpoznanie prawdziwych ustawień regionalnych).

Zastanawiałem się więc, czy to jest zbyt łatwe, zbyt idealne, jakie są wady? I trudno mi znaleźć odpowiedź. Więc przychodzę tu pisać:

tl; dr: Jakie są wady używania LC_CTYPE='C'konkretnej lokalizacji? Czy to źle? Czego powinienem się spodziewać?

Gregoire D.
źródło

Odpowiedzi:

26

Jakie są wady używania LC_CTYPE = „C” w stosunku do określonej lokalizacji

Dokumentacja wspomina o związku między ustawieniami regionalnymi a funkcjami SQL w obsłudze ustawień regionalnych :

Ustawienia regionalne wpływają na następujące funkcje SQL:

  • Sortuj kolejność w zapytaniach za pomocą ORDER BY lub standardowych operatorów porównania danych tekstowych

  • Funkcje górna, dolna i initcap

  • Operatory dopasowywania wzorców (wyrażenia regularne LIKE, SIMILAR TO i POSIX); ustawienia regionalne wpływają zarówno na dopasowanie bez rozróżniania wielkości liter, jak i na klasyfikację znaków według wyrażeń regularnych klasy znaków

  • Rodzina funkcji to_char

  • Możliwość używania indeksów z klauzulami LIKE

Pierwszy element (kolejność sortowania) jest o, LC_COLLATEa pozostałe wydają się być na temat LC_CTYPE.

LC_COLLATE

LC_COLLATEwpływa na porównania między łańcuchami. W praktyce najbardziej widocznym efektem jest porządek sortowania. LC_COLLATE='C'(lub POSIXktóry jest synonimem) oznacza, że ​​porównania są przeprowadzane w kolejności bajtów, natomiast ustawienia regionalne w language_REGIONformie oznaczają, że reguły kulturowe będą prowadzić porównania.

Przykład z francuskimi nazwami, wykonywany z bazy danych UTF-8:

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

Wynik:

 Imię 
-----------
 Béatrice
 bérénice
 Bernard
 Boris

béatricepojawia się wcześniej boris, ponieważ akcentowany E porównuje się z O tak, jakby był nieakcentowany. To reguła kulturowa.

Różni się to od tego, co dzieje się z Custawieniami regionalnymi:

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

Wynik:

 Imię 
-----------
 Bernard
 Boris
 Béatrice
 bérénice

Teraz nazwy z akcentem E są wypychane na końcu listy. Bajtowa reprezentacja éw UTF-8 jest szesnastkowa C3 A9i dla oniej 6f. c3jest większa niż 6ftak pod Clokalizacji, 'béatrice' > 'boris'.

To nie tylko akcenty. Istnieją bardziej złożone reguły z dzieleniem wyrazów, interpunkcją i takimi dziwnymi znakami œ. W każdym regionie można się spodziewać dziwnych zasad kulturowych.

Teraz, jeśli porównywane ciągi łączą różne języki, tak jak w przypadku firstnamekolumny dla ludzi z całego świata, może się zdarzyć, że określone ustawienia narodowe i tak nie powinny dominować, ponieważ różne alfabety dla różnych języków nie zostały zaprojektowane tak, aby były posortowane względem siebie.

W tym przypadku Cjest to racjonalny wybór i ma tę zaletę, że jest szybszy, ponieważ nic nie jest w stanie pokonać porównań czystych bajtów.

LC_CTYPE

Mając LC_CTYPEustawione na „c” oznacza, że funkcje takie jak C isupper(c)lub tolower(c)dać oczekiwane rezultaty tylko dla postaci w zakresie US-ASCII (czyli do kodowy 0x7F w Unicode).

Ponieważ funkcje SQL podoba upper(), lower()czy initcap są realizowane w PostgreSQL na szczycie tych funkcji libc, są one dotknięte przez to tak szybko, jak tam są dla znaków US-ASCII w ciągi.

Przykład:

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)

W przypadku Custawień regionalnych éjest traktowany jako znak niekwalifikowalny.

Podobnie błędne wyniki są również uzyskiwane z wyrażeniami regularnymi:

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

test=> select 'élysée' COLLATE "C" ~ '^\w+$';
 ?column? 
----------
 f
(1 row)
Daniel Vérité
źródło
Więc jeśli dobrze to zrozumiem, mielibyśmy problem z zamówieniem, nawet gdybyś zrobił serwer UTF-8? Wydaje mi się, że ustawienie systemu LC_CTYPE na UTF-8 lub kompilacja PostgreSQL w UTF-8 spowoduje ten sam problem z porównywaniem, jak wskazałeś.
Gregoire D.
W związku z tym, czy byłoby możliwe wymuszenie zestawiania zapytań, aby porównanie było lokalne, prawda?
Gregoire D.
Tak, poszczególne porównania łańcuchów mogą osadzać własne reguły zestawiania, tak jak w tej odpowiedzi collate "C"po order by. To Ty decydujesz, czy i gdzie Twoja aplikacja tego potrzebuje. Większość aplikacji tak naprawdę nie obchodzi.
Daniel Vérité
1
Należy również pamiętać, że poszczególne kolumny mogą mieć COLLATEspecyfikator różniący się od bazy danych.
Daniel Vérité
2
Ta odpowiedź jest naprawdę dla LC_COLLATE, a nie LC_CTYPE. LC_CTYPE służy do decydowania, czy znak jest cyfrą, literą,
białą
10

W odniesieniu do zaakceptowanej przez Daniela odpowiedzi na temat sortowania przy użyciu sortowania, należy pamiętać, że jeśli korzystasz z PostgreSQL na komputerze Mac, to preferowane sortowanie może nie działać zgodnie z oczekiwaniami z powodu nieodpowiednich ustawień niektórych zestawień na poziomie systemu operacyjnego. Możesz przeczytać więcej o tym problemie tutaj:

http://www.postgresql.org/message-id/[email protected]

To nie jest konkretny problem PostgreSQL, ale raczej problem z domyślną konfiguracją Mac dla ustawień sortowania. Mój obecny system działa pod PostgreSQL 9.3 na OS X El Capitan w wersji 10.11 i cierpi z powodu tego problemu. Mój system zwraca te same wyniki zapytania, niezależnie od tego, czy korzystam z sortowania „fr_FR” czy „en_US”. Na przykład:

Używając sortowania „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

Za pomocą sortowania „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

W moim systemie ustawienia sortowania (na poziomie systemu operacyjnego) są takie same dla „fr_FR” i „en_US”, jak pokazano w powłoce, uruchamiając diff:

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

Mamy nadzieję, że te dodatkowe informacje będą pomocne dla każdego, kto czyta ten, korzystający z PostgreSQL na komputerze Mac, który cierpi na ten problem.

cafecoder905
źródło
Jak mogę sprawić, by działał na nowoczesnych komputerach Mac. Czy przeszedłeś przez cokolwiek, aby działało na twoim komputerze Mac?
Dinesh Kumar,