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ć?

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)

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/4B4E845F.80906@postnewspapers.com.au

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.


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,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.