October 13, 2008

В ожидании 8.4 - lc_collation и lc_ctype уровня БД

Перевод Waiting for 8.4 - database-level lc_collation and lc_ctype с select * from depesz;

23 сентября Heikki Linnakangas применил патч, который написал Radek Strnad (на самом деле была применена его доработанная версия).

Что он делает? Патч позволяет добавлять (действительно!) разные collation order и character categories для разных БД.

До этого надо было устанавливать LC_COLLATE и LC_CTYPE при инициализации кластера (initdb), и в дальнейшем эти параметры невозможно было изменить. БД инициализированная с LATIN2 не будет корректно работать с данными в UTF-8.

Теперь всё меняется:
Делает LC_COLLATE и LC_CTYPE параметрами уровня БД. Порядок 
сортировки (collation) и класс символов (ctype) сейчас, подобно
кодировке, хранятся в новых колонках datcollate и datctype
таблицы database.

Это доработанная мной версия патча Radek Strnad'а.
Что мы получаем?

Например, предположим, что есть инстанс PostgreSQL инициализированный с локалью "C":
# \l
List of databases
Name | Owner | Encoding | Collation | Ctype | Access Privileges
-----------+--------+-----------+-----------+-------+----------------------------
depesz | depesz | SQL_ASCII | C | C |
postgres | pgdba | SQL_ASCII | C | C |
template0 | pgdba | SQL_ASCII | C | C | {=c/pgdba,pgdba=CTc/pgdba}
template1 | pgdba | SQL_ASCII | C | C | {=c/pgdba,pgdba=CTc/pgdba}
(4 rows)
Т.к. "C" ничего не знает, например, о польских символах, не возможно будет корректно сделать сортировку по польскому тексту. Это же касается работы upper():
# set client_encoding = 'UTF-8';
SET

# select c, upper(c) from (values ('a'), ('ć'), ('e'), ('ź'), ('x'), ('ł'), ('ś'), ('w')) as x (c) order by c;
c | upper
---+-------
a | A
e | E
w | W
x | X
ć | ć
ł | ł
ś | ś
ź | ź
(8 rows)
(Если вы не знакомы с польским алфавитом, просто поверьте мне. Сейчас я покажу как это должно выглядеть правильно.).

С версией postgres <= 8.3 мне бы потребовалось переинициализировать кластер (reinitdb), и, соответственно, сконвертировать все базы для новых языковых параметров, что немного проблематично.

К счастью, с версии 8.4 я смогу просто добавить новую базу с другими параметрами:
# CREATE DATABASE depesz_pl with encoding 'utf8' collate 'pl_PL.UTF-8' ctype 'pl_PL.UTF-8' template template0;
CREATE DATABASE
Единственная проблема в необходимости использования template0. Иначе я получу:
# CREATE DATABASE depesz_pl with encoding 'utf8' collate 'pl_PL.UTF-8' ctype 'pl_PL.UTF-8';
ERROR: new collation is incompatible with the collation of the template database (C)
HINT: Use the same collation as in the template database, or use template0 as template
(конечно же я мог бы сделать template1_pl, но не сейчас - как-нибудь в следующий раз :)

И так, теперь у нас есть новая БД, попробуем на ней наш тестовый запрос:
# \c depesz_pl
You are now connected to database "depesz_pl".

# show client_encoding ;
client_encoding
-----------------
UTF8
(1 row)

# select c, upper(c) from (values ('a'), ('ć'), ('e'), ('ź'), ('x'), ('ł'), ('ś'), ('w')) as x (c) order by c;
c | upper
---+-------
a | A
ć | Ć
e | E
ł | Ł
ś | Ś
w | W
x | X
ź | Ź
(8 rows)
ДА! Заработало!

Также, команда \l выдаст новую информацию:
# \l
List of databases
Name | Owner | Encoding | Collation | Ctype | Access Privileges
-----------+--------+-----------+-------------+-------------+----------------------------
depesz | depesz | SQL_ASCII | C | C |
depesz_pl | depesz | UTF8 | pl_PL.UTF-8 | pl_PL.UTF-8 |
postgres | pgdba | SQL_ASCII | C | C |
template0 | pgdba | SQL_ASCII | C | C | {=c/pgdba,pgdba=CTc/pgdba}
template1 | pgdba | SQL_ASCII | C | C | {=c/pgdba,pgdba=CTc/pgdba}
(5 rows)
Одна вещь, которую надо принять - нельзя изменить collation/ctype существующей базы. Причина тому очень проста: индексы зависят от collation (и могут зависеть от ctype). Т.о. изменение collation/ctype потребуют переиндексации всех данных. Если это технически возможно, вы сможете сделать это с помошью дампа базы, создания новой с желаемой локалью и загрузки этого дампа.

Конечно же, ещё далеко до полноценного функционирования PostgreSQL в мультиязычном окружении, но по крайней мере это шаг в правильном направлении. Долгожданный и важный шаг.

Комментарии

Steve, 29 сентября 2008 в 06:43
Вы упомянули "ещё далеко до полноценного функционирования PostgreSQL в мультиязычном окружении". Не могли бы вы рассказать в чём нехватка полноценного функционирования и где могут быть проблемы?

depesz, 29 сентября 2008 в 09:55
@Steve:
Для того чтобы сделать это полнофункциональным, необходима поддержка collation/ctype на более мелких объектах, чем база данных: таблицы или колонки.

Например, для сортировки по тексту на нескольких языках. Конечно, можно везде использовать utf8, но это приводит к дополнительным затратам на конвертацию текста при использовании различных кодировок.

3 comments:

Unknown said...

Не получается так и на 4 версии:

ts=# CREATE DATABASE depesz_pl with encoding 'utf8' collate 'pl_PL.UTF-8' ctype 'pl_PL.UTF-8' template template0;
ERROR: syntax error at or near "collate"


ts=# select version();
version
----------------------------------------------------------------------------------------------------------
PostgreSQL 8.4.2 on amd64-portbld-freebsd7.1, compiled by GCC cc (GCC) 4.2.1 20070719 [FreeBSD], 64-bit

grayhemp said...

Видимо в к релизу поменяли COLLATE/CTYPE на LC_COLLATE/LC_CTYPE.

http://www.postgresql.org/docs/8.4/interactive/sql-createdatabase.html

Unknown said...

ага, спасибо, так работает :)

Post a Comment