Поиск в SQLite без учета регистра, для русского языка

August 14th, 2010 Begemot

Не смотрю на всю свою юникодность, SQLite все-таки юникодна не до конца:( При поиске типа WHERE description LIKE “%STRING%” поиск идет без учета регистра для латинских букв, но к сожалению, он регистрозависимый для всех остальных символов. Корни этого уходят в виндовую функцию tolower(), которая правильно обрабатывает символы только текущей локали. И это весьма плохо, для целого ряда задач, например для поиска ранее скопированного текста в истории буфера обмена (извините, не удержался:))

Но к счастию способ есть. Оригинал и файлы для загрузки тут – SQLite and native UNICODE LIKE support in C/C++ . А я раскажу о том как это интегрировать и скрестить с wxDatabaselayer’om.

  1. Берем файлы sqlite3_unicode.h/c с архива по ссылке выше и добавляем в проект.
  2. Вызываем свойства sqlite3_unicode.c  и добавляем SQLITE_ENABLE_UNICODE; SQLITE_CORE в определения препроцессора c++/preprocessor/preprocessor definitions
  3. Если используете precompiled header в проекте, надо в свойствах обоих файлов отключить его использование.
  4. в с файле есть строчка #include “sqlite3.h” заменить ее на свой путь к sqlite3.h, если он у вас отличается
  5. Включить sqlite3.h в sqlite3_unicode.h, без этого у меня не компилилось.
  6. Дальше вызываете sqlite3_unicode_load();  \  sqlite3_unicode_free();  –  в начале/конце программы
  7. После открытия базы данных вызываем для нее sqlite3_unicode_init(db);

Проблема тут в том что sqlite3_unicode_init(db); хочет db типа sqlite3 *, а у нас есть только указатель на обьект датабайслаера. Тут придется править код самого wxDatabaselayer’a и заново компилить. Идем в databaselayer\include\SqliteDatabaseLayer.h, пишем:

// Added by Begemot – for using with  sqlite3_unicode
void * GetSQLiteDatabase()  { return m_pDatabase; } 

компиляем, и в нашей программе вместо строчки sqlite3_unicode_init(db); используем вот такую вот страшную конструкцию

sqlite3_unicode_init(static_cast<sqlite3*>(static_cast<SqliteDatabaseLayer*>(db)->GetSQLiteDatabase()));

Все. Теперь Like % прекрасно ищет по русским или любым другим символам без учета регистра.


Слушайте свою интуиции

March 30th, 2009 Begemot

Что отличает опытного разработчика от новичка? Правильно – хорошая интуиция 🙂

Только что ситуация на работе – надо написать сохранение проектов с которыми работает программа в базу данных. Как это все должно выглядеть обсудили еще в пятницу, начальник набросал табличку в которой все должно хранится. Я с утра сел – казалось бы куда проще – заимплементить дамп в бд, нескольких классов, учитывая что структура уже придумана и основной код сохранения в хмл написан.

Не работает, падает на pDB->PrepareStatement(…), убил 20 минут перепроверяя свои знания SQL, правильность написания полей и т.д. – все правильно, но не работает. Оказалось, что ему не нравится что одно из полей таблицы названо “Index”.  Не знаю в чем трабла в SQLite или в используемом враппере wxSqlite3, или еще в чем… но факт что падает на строке

st = pDB->PrepareStatement(_T(“INSERT INTO MMFrames (ProjectID, Index,  Type, Properties) VALUES (?, ?, ?, ?);”));

Пришлось переименовывать поле в таблице.

А весь юмор ситуации в том что, когда создавал бд – я подумал что лучше вместо Index назвать поле Position. Но решил что раз начальник написал Index, пусть так и будет. Потом подумал, что это поле вообще тут не нужно и хотел его удалить, но опять же решил оставить, раз на бумажке есть. Вообщем доверяйте своей интуиции.


Сюрпризы sqlite CURRENT_TIMESTAMP

December 13th, 2008 Begemot

Думаете достаточно просто определить стобец как

created TIMESTAMP not null default CURRENT_TIMESTAMP,

и забыть про него. В смысле не задавать значения этого поля при вставке, мол sqlite сама туда добавит текущее время? Напрасно 🙂

SQLite действительно сама занесет в него текущее время при вставке строки, но это будет время в UTC, без учета вашего смещения. В таком случае читать его надо как

SELECT datetime(created, 'localtime') FROM my_table; 

вместо обычного

SELECT created FROM my_table;

Но я думаю что лучше сразу правильно писать в дб, указывая значения для этого столбца как datetime(‘now’, ‘localtime’). В коде использованием SqliteDatabaseLayer, это будет выглядеть примерно так

PreparedStatement* pStatement = db->PrepareStatement(wxT(“INSERT INTO log (created, title) VALUES (datetime(‘now’, ‘localtime’), ?);”));

Больше про функции работы с датой в sqlite.