PHP, SQLite и регистронезависимость

Несмотря на объявленную поддержку UTF, встраиваемая СУБД SQLite3 по умолчанию не умеет делать регистронезависимую сортировку, сравнение и операции по преобразованию строк над буквами, не входящими в английский алфавит. Для решения этой проблемы разработчики SQLite рекомендуют использовать расширение ICU, с подключением которого станет возможным выбирать различные кодировки и иметь возможность пользоваться регистронезависимыми операциями сравнения любых символов кроме латиницы. Вот только описаний, как это сделать для php-mod-sqlite3, найти толком нельзя, да и объем ICU превышает размер самой библиотеки SQLite! Для embedded-применений – например, в OpenWrt – такое не годится.

Давным-давно, в далекой-далекой галактике… оказалось, что много лет назад некий товарищ по имени ioannis в рамках решения означенной проблемы создал небольшое расширение SQLite, по сути представляющее собой урезанную версию ICU, из которой был выброшен код, который в 95% случаев никогда не потребуется. Таким образом удалось сократить размер расширения с мегабайт до символических 70-90 кбайт (в зависимости от архитектуры и компилятора). Копия исходного сообщения осталась доступна на mail-archive.com, и хотя оригинальная ссылка на исходный код расширения sqlite3_unicode.c неработоспособна, его все еще можно найти в интернете, а также мы добавили его в наш архив.

Возможности расширения

#define SQLITE3_UNICODE_FOLD – поддержка нелатинских символов функцией FOLD() – +10КБ
#define SQLITE3_UNICODE_LOWER – поддержка нелатинских символов функцией LOWER() – +10КБ
#define SQLITE3_UNICODE_UPPER – поддержка нелатинских символов функцией UPPER() – +10КБ
#define SQLITE3_UNICODE_TITLE – поддержка нелатинских символов функцией TITLE() – +10КБ
#define SQLITE3_UNICODE_UNACC – преобразование accented-символов в обычные, для русского языка не очень актуально – +30КБ
#define SQLITE3_UNICODE_COLLATE – заменяет COLLATION типа NOCASE на новый, в котором при сравнении не учитывается регистр в т.ч. и для нелатинских символов.
#define SQLITE3_UNICODE_UNACC_AUTOMATIC – попытаться применять UNACC в функции LIKE и операциях сравнения с COLLATE NOCASE.

Что характерно, функциональность, которая не требуется, может быть отключена путем убирания соответствующих #define для экономии места и увеличения производительности (вряд ли, впрочем, существенного).

Теперь разберемся, как собрать это расширение и задействовать его в cli-версии SQLite и в модуле для PHP.

Сборка под Linux

Для начала, скачиваем и распаковываем исходный код SQLite – нам потребуются заголовочные файлы из него. Можно использовать любую версию: я брал amalgamation без autoconf – она меньше. В тот же каталог копируем sqlite3_unicode.c.

Далее, корректности ради, открываем sqlite3_unicode.c текстовым редактором и вписываем вверху директиву:

/* we want shared library. only. */
#define SQLITE_ENABLE_UNICODE 1

Этим мы указываем, что собираемся компилировать shared-библиотеку. С другой стороны – соответствующий #if в коде расширения написан таким своеобразным образом, что этого можно и не делать.

Затем следует непосредственно процесс компиляции. Предполагается, что пакеты из набора build-essentials уже установлены в системе – во всяком случае, здесь потребуется gcc:

gcc -shared -o sqlite3_unicode.so sqlite3_unicode.c

Закрывая глаза на ряд warning’ов, получаем готовую библиотеку.

При работе с cli-версией потребуется разместить полученный sqlite3_unicode.so в каталоге с остальными системными библиотеками, либо же выполнить export следующего вида:

export LD_LIBRARY_PATH="/path/to/library/dir:$LD_LIBRARY_PATH"

Затем открываем cli и пишем команду .load sqlite3_unicode. Если ошибок не возникло – значит, расширение успешно загрузилось.

Для загрузки расширения в PHP потребуется в php.ini выставить корректный путь к месту расположения библиотеки в перараметре sqlite3.extension_dir. Затем в скрипте используем команду вида $db->loadExtension(‘sqlite3_unicode.so’);

Вот и всё – теперь сравнения с COLLATE NOCASE будут регистронезависимыми не только для латиницы, но и для русских букв.

Сборка под OpenWrt

Теперь попробуем побороть Buildroot от OpenWrt и кросс-компилировать это расширение в toolchain’е для запуска на роутере. В конечном счете, именно ради этого весь процесс и затевался – на ПК-то нет проблемы установить ICU, в отличие от встраиваемых систем. Надо признать, что борьба с Buildroot’ом для меня оказалась намного сложнее, чем все поиски решения проблемы с регистрозависимостью в целом и попытки разобраться, как его собирать в обычных условиях, в частности.

В конечном счете, после бессчетных неудачных попыток, я остановился на варианте формирования отдельного пакета, содержащего только это расширение с выставленным параметром DEPENDS на libsqlite3 и включенным в его состав С-файле с исходным кодом. Скачать готовый пакет можно в нашем архиве.

Его можно распаковать в каталог “package” корневой директории Buildroot’а, а затем нужно в make menuconfig выбрать пакет libsqlite3_unicode и запустить его сборку при помощи команды make package/libsqlite3_unicode/compile.

После установки полученного ipk на роутер подключение данного расширения производится аналогичным ранее описанному способом. Обратите внимание, что имя расширения для OpenWrt – libsqlite3_unicode.so, его размещение по умолчанию – /usr/lib.

This entry was posted in Сеть и интернет and tagged , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>