Регистронезависимый LIKE в SQLite для кириллицы

При использовании LIKE или ORDER BY в SQLite с Unicode-символами может возникнуть проблема, связанная с неспособностью SQLite по умолчанию правильно воспринимать разные регистры одной и той же буквы:

sqlite> select "ы" like "ы";
1
sqlite> select "Ы" like "ы";
0

Поиск приводит к расширению ICU, которое переопределяет функции изменения регистра так, чтобы символы за пределами таблицы ASCII правильно приводились к заданному регистру.

Есть два варианта использования расширения:

  1. Скомпилировать SQLite вместе с расширением (и использовать вместо системной библиотеки).
  2. Скомпилировать расширение как Runtime Loadable Extension.

Мне хотелось использовать расширение через стандартный модуль sqlite3 в Python, поэтому было решено пойти по второму пути.

Для начала установим необходимые пакеты с исходным кодом:

$ sudo apt-get install libicu-dev libsqlite3-dev

Скачаем исходный код самого расширения:

$ wget "https://www.sqlite.org/src/raw/ext/icu/icu.c?name=b2732aef0b076e4276d9b39b5a33cec7a05e1413" -O icu.c

А дальше — немного странный момент. Если мы сейчас скомпилируем расширение, то вероятно, получим следующую ошибку при попытке загрузить его (имя в вашей версии SQLite может быть другим):

sqlite> .load ./libsqliteicu.so
Error: ./libsqliteicu.so: undefined symbol: sqlite3_sqliteicu_init

Чтобы исправить ошибку, редактируем файл icu.c, заменяя в нем имя функции sqlite3_icu_init на sqlite3_sqliteicu_init.

Теперь собираем расширение:

$ gcc -shared icu.c -g -o libsqliteicu.so -fPIC `icu-config --cppflags --ldflags`

Загружаем расширение и проверяем, что все в порядке:

sqlite> .load ./libsqliteicu.so
sqlite> select "Ы" like "ы";
1

Бонус

Для того, чтобы загрузить это расширение из Python, сначала разрешаем загрузку расширений из библиотек и затем загружаем саму библиотеку:

import sqlite3
connection = sqlite3.connect(":memory:")
connection.enable_load_extension(True)
connection.load_extension("./libsqliteicu.so")
assert connection.execute("select 'ы' like 'Ы'").fetchone() == (1, )