Как заставить PostgreSQL правильно сортировать UTF8 кириллицу на Mac OS X
Как известно, PostgreSQL очень капризен к окружению в котором он работает. Он использует настройки окружения для определения правил сортировки, преобразования в строчные и заглавные буквы, работы с датами и т.д. Точнее эти настройки он высасывает из окружения в момент инициализации кластера, и в дальнейшем они уже не имеют значения.
Я работаю под Mac OS X 10.6.4. Как все нормальные люди, я пользуюсь кодировкой UTF8. Но как оказалось, кириллические данные в этой кодировке в постгресе сортируются некорректно... Сразу стало понятно, что это проблема окружения. Через некоторое время, я выяснил что правила сортировки для локали ru_RU.UTF-8 в системе просто отсутствуют... Точнее файл LC_COLLATE, в котором должны храниться эти настройки, является символической ссылкой на файл из латинской локали.
[cc lang="bash"]
~ $ ls -al /usr/share/locale/ru_RU.UTF-8/
lrwxr-xr-x 1 root wheel 28 Sep 26 2009 LC_COLLATE -> ../la_LN.US-ASCII/LC_COLLATE
[/cc]
Я перепробовал кучу вариантов: подменял этот файл файлом из другой русской локали (CP1251, KOI8-R и т.д.), нашел вариант этого файла, который корректно работает для FreeBSD (они из одного семейства с Mac OS X), извращался с локалями как только мог - все безрезультатно...
Хотел было уже плюнуть на это дело, поскольку под линуксом (на который планируется деплой) все работает нормально. Но на последок решил проверить решение, на которое меня натолкнула ветка на форуме Apple. И оно сработало! Решение весьма оригинальное, возможно, не идеальное, но работает! Судите сами...
Сначала все как обычно:
[cc lang="bash"]
$ sudo port install postgresql84-server
$ sudo mkdir -p /opt/local/var/db/postgresql84/defaultdb
$ sudo chown postgres:postgres /opt/local/var/db/postgresql84/defaultdb
[/cc]
Далее нужно инициализировать кластер, но перед этим необходимо настроить правильное окружение:
[cc lang="bash"]
$ export LC_ALL=ru_RU.UTF-8
$ export LANG=ru_RU.UTF-8
$ locale
LANG="ru_RU.UTF-8"
LC_COLLATE="ru_RU.UTF-8"
LC_CTYPE="ru_RU.UTF-8"
LC_MESSAGES="ru_RU.UTF-8"
LC_MONETARY="ru_RU.UTF-8"
LC_NUMERIC="ru_RU.UTF-8"
LC_TIME="ru_RU.UTF-8"
LC_ALL="ru_RU.UTF-8"
[/cc]
Итак, сейчас окружение настроено на нужную нам русскую локаль с кодировкой UTF8, но в этой кодировке нет правил сортировки для кириллицы. Как быть? Я решил попробовать делать
сортировку на основе порядка кодов в кодовой таблице UTF8. В большинстве случаев это дает вполне корректный результат. Для этого надо заставить Postgres использовать для сопоставлений (collation) родные правила языка С. Делается это так:
[cc lang="bash"]
$ export LC_COLLATE=C
$ unset LC_ALL
$ locale
LANG="ru_RU.UTF-8"
LC_COLLATE="C"
LC_CTYPE="ru_RU.UTF-8"
LC_MESSAGES="ru_RU.UTF-8"
LC_MONETARY="ru_RU.UTF-8"
LC_NUMERIC="ru_RU.UTF-8"
LC_TIME="ru_RU.UTF-8"
LC_ALL=
[/cc]
Теперь инициализируем кластер:
[cc lang="bash"]
$ sudo su postgres -c '/opt/local/lib/postgresql84/bin/initdb -D /opt/local/var/db/postgresql84/defaultdb'
The files belonging to this database system will be owned by user "postgres".
This user must also own the server process.
The database cluster will be initialized with locales
COLLATE: C
CTYPE: UTF-8
MESSAGES: ru_RU.UTF-8
MONETARY: ru_RU.UTF-8
NUMERIC: ru_RU.UTF-8
TIME: ru_RU.UTF-8
The default database encoding has accordingly been set to UTF8.
initdb: could not find suitable text search configuration for locale UTF-8
The default text search configuration will be set to "simple".
[/cc]
Теперь постгрес готов к работе и сортировка работает как надо! Возможно здесь есть побочные эффекты, но я пока не обнаружил. А Вы?
P.S.
Обнаружена некорректная сортировка буквы ё/Ё: "Ё" сортируется перед "А", а "ё" после "я"
Leave a comment