Який вплив LC_CTYPE на базу даних PostgreSQL?


25

Отже, у мене декілька серверів Debian з PostgreSQL на ньому. Історично склалося, що ці сервери та PostgreSQL локалізовані з діаграмою Latin 9 і тоді це було добре. Тепер ми маємо поводитися з такими речами, як польська, грецька чи китайська, тому зміна цього питання стає все більшою проблемою.

Коли я спробував створити базу даних UTF8, я отримав повідомлення:

ПОМИЛКА: кодування UTF8 не відповідає локалі fr_FR Детальніше: Вибране налаштування LC_CTYPE вимагає кодування LATIN9.

Кілька разів я робив кілька досліджень на цю тему зі своїм старим партнером Google, і все, що я міг знайти, - це кілька надскладних процедур, таких як оновлення Debian LANG, перекомпіляція PostgreSQL з правильним набором, редагування всіх LC_системних змінних та інших незрозумілих рішень. Тож поки що це питання ми відкидаємо.

Нещодавно він знову повернувся, греки хочуть цього матеріалу, а латинська 9 не хоче. І коли я ще раз переглядав це питання, один колега підійшов до мене і сказав: «Ну, легко, дивись».

Він нічого не редагував, не робив магічних трюків, просто робив цей 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;

І це спрацювало чудово.

Я насправді не знав про це, LC_CTYPE='C'і мене здивувало, що використання цього не було в перших рішеннях в Google і навіть у стеці Overflow. Я озирнувся і знайшов згадку лише в документації PostgreSQL.

Якщо LC_CTYPE дорівнює C або POSIX, будь-який набір символів дозволений, але для інших налаштувань LC_CTYPE існує лише один набір символів, який буде працювати правильно. Оскільки параметр LC_CTYPE заморожено initdb, очевидна гнучкість використання різних кодувань у різних базах даних кластеру є більш теоретичною, ніж реальною, за винятком випадків, коли ви вибираєте C або POSIX-локаль (тим самим вимикаючи будь-яку реальну інформацію про локаль).

Тож мене змусило задуматися, це занадто легко, занадто досконало, які недоліки? І мені ще важко знайти відповідь. Тож ось я приходжу дописи тут:

tl; dr: Які недоліки використання LC_CTYPE='C'у певній локалізації? Чи погано це робити? Що я повинен розраховувати на розрив?

Відповіді:


26

Які недоліки використання LC_CTYPE = 'C' для певної локалізації

У документації згадується взаємозв'язок між локалями та функціями SQL у службі підтримки локальної служби :

Параметри локалі впливають на такі функції SQL:

  • Сортуйте порядок у запитах за допомогою ORDER BY або стандартних операторів порівняння текстових даних

  • Верхня, нижня та функція initcap

  • Оператори відповідності шаблонів (регулярні вирази стилю LIKE, SIMILAR TO та POSIX); локалі впливають як на нечутливе до регістру відповідність, так і на класифікацію символів за регулярними виразами класів символів

  • Сімейство функцій to_char

  • Можливість використання індексів із пропозиціями LIKE

Перший елемент (порядок сортування) про, LC_COLLATEа інші, здається, про всі LC_CTYPE.

LC_COLLATE

LC_COLLATEвпливає на порівняння між рядками. На практиці найбільш видимим ефектом є порядок сортування. LC_COLLATE='C'(або POSIXщо є синонімом) означає, що порядок байтів визначає порівняння, тоді як локал у language_REGIONформі означає, що культурні правила керуватимуть порівняннями.

Приклад із французькими іменами, виконаними з внутрішньої бази даних UTF-8:

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

Результат:

 ім'я 
-----------
 беатріче
 bérénice
 Бернар
 бор

béatriceприходить раніше boris, тому що наголошений E порівнює проти O так, як якщо б він не був наголошеним. Це культурне правило.

Це відрізняється від того, що відбувається з Cлокальним:

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

Результат:

 ім'я 
-----------
 Бернар
 бор
 беатріче
 bérénice

Тепер імена з наголосом E висуваються в кінці списку. Байтове представлення éв UTF-8 є шістнадцятковим C3 A9і для oнього 6f. c3більше , ніж 6fце під Cлокалі 'béatrice' > 'boris'.

Це не просто акценти. Існують складніші правила з переносом, пунктуацією та дивними символами œ. Дивні культурні правила слід очікувати в кожному регіоні.

Тепер, якщо рядки для порівняння трапляються для змішування різних мов, як, наприклад, коли у них firstnameстовпець для людей з усього іншого світу, можливо, будь-яка конкретна мова не повинна домінувати, так чи інакше, тому що різні алфавіти для різних мов не розроблені так, щоб бути сортували один проти одного.

У цьому випадку Cце раціональний вибір, і він має перевагу в тому, щоб бути швидшим, тому що ніщо не може перемогти чисто порівняння байтів.

LC_CTYPE

Якщо LC_CTYPEвстановити значення "C", це означає, що C функціонує як isupper(c)або tolower(c)дає очікувані результати лише для символів у діапазоні US-ASCII (тобто до кодової точки 0x7F в Unicode).

Оскільки функції SQL подобається upper(), lower()або initcap реалізуються в Postgres на вершині цих LibC функцій, вони страждають від цього , як тільки є символом не US-ASCII в рядках.

Приклад:

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)

Для Cмісцевого éзначення трактується як категоризаційний персонаж.

Аналогічно неправильні результати також отримуються з регулярними виразами:

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

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

Отже, якщо я зрозумію, у нас виникне проблема з замовленням, навіть якщо ви зробили сервер UTF-8? Я думаю, що встановлення системи LC_CTYPE на UTF-8 або компіляція PostgreSQL в UTF-8 призведе до тієї ж проблеми порівняння, що і ви вказали.
Грегуар Д.

Щоб розширити це, чи можна було б змусити порівнювати запити, щоб порівняння було локальним?
Gregoire D.

Так, у порівняльних рядкових порівняннях можуть вбудовуватися власні правила зіставлення, як я це роблю в цій відповіді з collate "C"після order by. Ви самі вирішуєте, чи потребує це ваша програма. Більшість додатків там насправді не цікавлять.
Даніель Веріте

1
Також зауважте, що окремі стовпці можуть мати COLLATEспецифікатор, який відрізняється від баз даних.
Даніель Веріте

2
Ця відповідь дійсно стосується LC_COLLATE, а не LC_CTYPE. LC_CTYPE використовується для визначення того, чи символом є цифра, літера, пробіл, розділові знаки тощо
jjanes

10

Посилаючись на прийняту відповідь Даніеля про сортування за допомогою порівнянь, врахуйте, що якщо ви використовуєте PostgreSQL на Mac, то ваше бажане порівняння може не працювати, як ви очікуєте, через неадекватні налаштування для деяких зіставлень на рівні операційної системи. Детальніше про проблему ви можете прочитати тут:

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

Спеціально це не проблема PostgreSQL, а скоріше проблема конфігурації Mac за замовчуванням для налаштувань порівняння. Моя поточна система працює з PostgreSQL 9.3 на OS X El Capitan версії 10.11 і страждає від цієї проблеми. Моя система повертає ті самі результати запиту, незалежно від того, використовую я порівнювальну функцію "fr_FR" або "en_US". Наприклад:

Використання порівняння "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

Використання порівняння "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

У моїй системі параметри зіставлення (на рівні операційної системи) однакові для "fr_FR" та "en_US", як показано в оболонці, виконуючи diff:

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

Сподіваємось, ця додаткова інформація корисна для тих, хто читає це, хто використовує PostgreSQL на Mac, який страждає від цієї проблеми.


Як змусити його працювати в сучасних Mac. Ви пережили щось, щоб це працювало у вашому комп'ютері?
Дінеш Кумар
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.