QSqlDriver + Sqlite + case insensitivity
От: Alexey F  
Дата: 05.03.10 01:09
Оценка:
Здравствуйте!

Возникла проблема — хочу использовать sqlite для базы данных через QSql, но вот беда — в стандартной, а, стало быть и в Qt-шной поставке полноценная поддержка unicode в комплект не входит
Платформа — Windows, версия Qt — 4.6.0, версия sqlite в нём — 3.6.19. Собран sqlite как отдельный плагин (dll), причём ни одна из sqlite* ф-ций из таблицы экспорта не выглядывает (т.е. предлагаемый Qt Assistant трюк с:
 QSqlDatabase db = ...;
 QVariant v = db.driver()->handle();
 if (v.isValid() && qstrcmp(v.typeName(), "sqlite3*")==0) {
     // v.data() returns a pointer to the handle
     sqlite3 *handle = *static_cast<sqlite3 **>(v.data());
     if (handle != 0) { // check that it is not NULL
         ...
     }
 }

бесполезен).

Решение проблемы вижу в том, чтобы подправить исходники qsql_sqlite.* (qt\src\sql\drivers\sqlite), в которых переопределить ф-ции upper, lower и collate NOCASE (кажется, так, но я могу ошибаться; Для like в sqlite достаточно NOCASE задать или лучше к uppercase обе строки перед сравнением приводить?). И пересобрать таким образом плагин. А т.к. это LGPL, с дистрибутивом программы положить архив с изменёнными исходниками (или для соблюдения лицензии надо что-то ещё?).

Пока искал по сети, встречал готовые DLL с поддержкой unicode, слинкованные с ICU. Стоит попробовать прикрутить их или зря время потрачу?

Вопрос — кто-нибудь добавлял поддержку unicode (конкретно для регистронезависимого поиска) в драйвер sqlite для Qt? Есть более красивое или даже готовое решение (гуглил — не нашёл )?

P.S. FTS3, case-insensitivity работающую с utf-8, замучаюсь прикручивать или тоже стоит попробовать?
sqlite qt plugin unicode support
Re: QSqlDriver + Sqlite + case insensitivity
От: fdn721  
Дата: 05.03.10 03:58
Оценка: 1 (1)
Здравствуйте, Alexey F

(18) Case-insensitive matching of Unicode characters does not work.

The default configuration of SQLite only supports case-insensitive comparisons of ASCII characters. The reason for this is that doing full Unicode case-insensitive comparisons and case conversions requires tables and logic that would nearly double the size of the SQLite library. The SQLite developers reason that any application that needs full Unicode case support probably already has the necessary tables and functions and so SQLite should not take up space to duplicate this ability.

Instead of providing full Unicode case support by default, SQLite provides the ability to link against external Unicode comparison and conversion routines. The application can overload the built-in NOCASE collating sequence (using sqlite3_create_collation()) and the built-in like(), upper(), and lower() functions (using sqlite3_create_function()). The SQLite source code includes an "ICU" extension that does these overloads. Or, developers can write their own overloads based on their own Unicode-aware comparison routines already contained within their project.

Re: QSqlDriver + Sqlite + case insensitivity
От: Ziaw Россия  
Дата: 05.03.10 07:24
Оценка: 5 (1)
Здравствуйте, Alexey F, Вы писали:

AF>Пока искал по сети, встречал готовые DLL с поддержкой unicode, слинкованные с ICU. Стоит попробовать прикрутить их или зря время потрачу?


А не проще взять субд поддерживающую юникод? Тот же firebird embedded по удобству не отличается от sqlite, зато вполне взрослая субд. Размер правда больше, но не фатально.
Re[2]: QSqlDriver + Sqlite + case insensitivity
От: Alexey F  
Дата: 05.03.10 09:21
Оценка:
Здравствуйте, fdn721, Вы писали:

F>

(18) Case-insensitive matching of Unicode characters does not work.
[]


В sqlite-вский FAQ я конечно же смотрел
Или это ответ на

Для like в sqlite достаточно NOCASE задать или лучше к uppercase обе строки перед сравнением приводить?

?
Каюсь, про строку "The application can overload the built-in NOCASE collating sequence (using sqlite3_create_collation()) and the built-in like(), upper(), and lower() functions (using sqlite3_create_function())." вчера перед сном забыл.

Ещё же в процессе гугления было упоминание о каких-то граблях со сборкой sqlite+ICU под windows, поэтому счёл нужным спросить RSDN.
Re[2]: QSqlDriver + Sqlite + case insensitivity
От: Alexey F  
Дата: 05.03.10 09:24
Оценка:
Здравствуйте, Ziaw, Вы писали:

Z>А не проще взять субд поддерживающую юникод? Тот же firebird embedded по удобству не отличается от sqlite, зато вполне взрослая субд. Размер правда больше, но не фатально.

Не получится с sqlite — гляну firebird, спасибо за наводку.
Разницы мне действительно особенной нет, просто прикрутить регистронезависимый поиск к sqlite вчера показалось проще, чем взять другую БД.
Сегодня ещё немного попробую покопать в этом направлении.
Re[3]: QSqlDriver + Sqlite + case insensitivity
От: trdm Россия  
Дата: 05.03.10 09:46
Оценка:
Здравствуйте, Alexey F, Вы писали:
AF>Не получится с sqlite — гляну firebird, спасибо за наводку.
где тут раздают на водку?
Re[4]: QSqlDriver + Sqlite + case insensitivity
От: Alexey F  
Дата: 05.03.10 09:47
Оценка: +1 :)
Здравствуйте, trdm, Вы писали:

T>где тут раздают на водку?

Жалко, нет смайлика 'O_o' или в оценках.

НАВОДКА -и, ж. 1. см. навести. 2. Придание стволу артиллерийского орудия положения, необходимого для попадания в цель (спец.). Битъ прямой наводкой.

Re: Нормальное решение
От: Alexey F  
Дата: 05.03.10 15:45
Оценка:
Здравствуйте, Alexey F, Вы писали:

Тот пост, что писал раньше, удалил, теперь по-нормальному.

По запросу в google "sqlite icu windows" выходим первой же ссылкой на: http://www.urban-eye.com/pagesqliteicu.html. Я взял версию 3.6.22. Кстати, там же, чуть ниже поищите по строке "icudt38.dll" и скачайте прилагающийся архив. Оттуда берём 3-хкилобайтовый файл icudt38.dll (он почему-то не был приложен в архиве с версией 3.6.22).

Потом идём на http://www.sqlite.org/download.html и скачиваем amalgamation-архив с той же версией, что и прекомпилированный бинарник.

Договоримся, что в %QTDIR% у нас записан путь туда, где установлена Qt с папкой src внутри (в моём случае это "...\Qt\2009.05\qt").

В %QTDIR%/3rdparty создаём папку sqliteicu, туда распаковываем sqlite*.* из amalgamation-архива с sqlite.org. Там же поместим lib-файл к sqlite3.dll (для mingw-gcc описание этой процедуры здесь).
На всякий случай def-файл для sqlite3.dll:

  Скрытый текст
LIBRARY SQLITE3.DLL
EXPORTS
sqlite3_aggregate_context
sqlite3_aggregate_count
sqlite3_auto_extension
sqlite3_bind_blob
sqlite3_bind_double
sqlite3_bind_int
sqlite3_bind_int64
sqlite3_bind_null
sqlite3_bind_parameter_count
sqlite3_bind_parameter_index
sqlite3_bind_parameter_name
sqlite3_bind_text
sqlite3_bind_text16
sqlite3_bind_value
sqlite3_bind_zeroblob
sqlite3_blob_bytes
sqlite3_blob_close
sqlite3_blob_open
sqlite3_blob_read
sqlite3_blob_write
sqlite3_busy_handler
sqlite3_busy_timeout
sqlite3_changes
sqlite3_clear_bindings
sqlite3_close
sqlite3_collation_needed
sqlite3_collation_needed16
sqlite3_column_blob
sqlite3_column_bytes
sqlite3_column_bytes16
sqlite3_column_count
sqlite3_column_decltype
sqlite3_column_decltype16
sqlite3_column_double
sqlite3_column_int
sqlite3_column_int64
sqlite3_column_name
sqlite3_column_name16
sqlite3_column_text
sqlite3_column_text16
sqlite3_column_type
sqlite3_column_value
sqlite3_commit_hook
sqlite3_complete
sqlite3_complete16
sqlite3_config
sqlite3_context_db_handle
sqlite3_create_collation
sqlite3_create_collation16
sqlite3_create_collation_v2
sqlite3_create_function
sqlite3_create_function16
sqlite3_create_module
sqlite3_create_module_v2
sqlite3_data_count
sqlite3_db_config
sqlite3_db_handle
sqlite3_db_mutex
sqlite3_db_status
sqlite3_declare_vtab
sqlite3_enable_load_extension
sqlite3_enable_shared_cache
sqlite3_errcode
sqlite3_errmsg
sqlite3_errmsg16
sqlite3_exec
sqlite3_expired
sqlite3_extended_errcode
sqlite3_extended_result_codes
sqlite3_file_control
sqlite3_finalize
sqlite3_free
sqlite3_free_table
sqlite3_get_autocommit
sqlite3_get_auxdata
sqlite3_get_table
sqlite3_global_recover
sqlite3_initialize
sqlite3_interrupt
sqlite3_last_insert_rowid
sqlite3_libversion
sqlite3_libversion_number
sqlite3_limit
sqlite3_load_extension
sqlite3_malloc
sqlite3_memory_alarm
sqlite3_memory_highwater
sqlite3_memory_used
sqlite3_mprintf
sqlite3_mutex_alloc
sqlite3_mutex_enter
sqlite3_mutex_free
sqlite3_mutex_leave
sqlite3_mutex_try
sqlite3_next_stmt
sqlite3_open
sqlite3_open16
sqlite3_open_v2
sqlite3_os_end
sqlite3_os_init
sqlite3_overload_function
sqlite3_prepare
sqlite3_prepare16
sqlite3_prepare16_v2
sqlite3_prepare_v2
sqlite3_profile
sqlite3_progress_handler
sqlite3_randomness
sqlite3_realloc
sqlite3_release_memory
sqlite3_reset
sqlite3_reset_auto_extension
sqlite3_result_blob
sqlite3_result_double
sqlite3_result_error
sqlite3_result_error16
sqlite3_result_error_code
sqlite3_result_error_nomem
sqlite3_result_error_toobig
sqlite3_result_int
sqlite3_result_int64
sqlite3_result_null
sqlite3_result_text
sqlite3_result_text16
sqlite3_result_text16be
sqlite3_result_text16le
sqlite3_result_value
sqlite3_result_zeroblob
sqlite3_rollback_hook
sqlite3_set_authorizer
sqlite3_set_auxdata
sqlite3_shutdown
sqlite3_sleep
sqlite3_snprintf
sqlite3_soft_heap_limit
sqlite3_sql
sqlite3_status
sqlite3_step
sqlite3_stmt_status
sqlite3_test_control
sqlite3_thread_cleanup
sqlite3_threadsafe
sqlite3_total_changes
sqlite3_trace
sqlite3_transfer_bindings
sqlite3_update_hook
sqlite3_user_data
sqlite3_value_blob
sqlite3_value_bytes
sqlite3_value_bytes16
sqlite3_value_double
sqlite3_value_int
sqlite3_value_int64
sqlite3_value_numeric_type
sqlite3_value_text
sqlite3_value_text16
sqlite3_value_text16be
sqlite3_value_text16le
sqlite3_value_type
sqlite3_version
sqlite3_vfs_find
sqlite3_vfs_register
sqlite3_vfs_unregister
sqlite3_vmprintf
sqlite3_win32_mbcs_to_utf8



Я назвал результат libsqlite3dll.a.
Далее в %QTDIR%\src\sql\drivers копируем папку sqlite в sqliteicu, все вхождения QSQLite заменяем на QSQLiteICU в файлах qsql_sqlite*.
Попутно в файле qsql_sqlite.cpp заменяем:
#include <sqlite3.h>

на:
#define SQLITE_API __declspec(dllexport)
#include <sqlite3.h>


В %QTDIR%\src\plugins\sqldrivers копируем sqlite в sqliteicu. Я поменял имена *.rc-файлов, заменив sqlite на sqliteicu.
В файле %QTDIR%\src\plugins\sqldrivers\sqliteicu\smain.cpp делаем следующие изменения:
QSQLite меняем на QSQLiteICU, QLatin1String("QSQLITE") заменяем на QLatin1String("QSQLITEICU").

Компилируем так:
cd %QTDIR%\qt\src\plugins\sqldrivers\sqliteicu
qmake "INCLUDEPATH+=../../../3rdparty/sqliteicu" "LIBS+=-L../../../3rdparty/sqliteicu -lsqlite3dll"


В файле sqlite.pro (у меня он переименован в sqliteicu.pro), в папке %QTDIR%\src\plugins\sqldrivers\sqliteicu:
TARGET     = qsqliteicu

HEADERS        = ../../../sql/drivers/sqliteicu/qsql_sqlite.h
SOURCES        = smain.cpp \
          ../../../sql/drivers/sqliteicu/qsql_sqlite.cpp

!system-sqliteicu:!contains( LIBS, .*sqliteicu.* ) {
    CONFIG(release, debug|release):DEFINES *= NDEBUG
    DEFINES += SQLITE_OMIT_LOAD_EXTENSION SQLITE_OMIT_COMPLETE 
    INCLUDEPATH += ../../../3rdparty/sqliteicu
} else {
    LIBS *= $$QT_LFLAGS_SQLITE
    QMAKE_CXXFLAGS *= $$QT_CFLAGS_SQLITE
}

include(../qsqldriverbase.pri)


Теперь, вроде, ничего не забыл

Все icu*.dll-файлы не забываем положить в одну из простреливаемых переменной окружения %PATH% папок
Проверял работу dll через %QTDIR%/demos/sqlbrowser, подключив там QSQLITEICU. upper, lower, like, order by работают правильно:
create table Test values ( content text );
insert into Test values ( 'Строка' );
insert into Test values ( 'СтРОка' );
insert into Test values ( 'строкА' );

select upper ( context ) from Test;
...


P.S. FTS3 не прикручивал.
Re[2]: Нормальное решение
От: Alexey F  
Дата: 10.03.10 12:17
Оценка:
Здравствуйте, Alexey F, Вы писали:

Правда, collate nocase от этого на unicode-строках не заработал . Только с lower/upper.
Re[2]: Нормальное решение
От: Аноним  
Дата: 14.10.11 07:41
Оценка:
Здравствуйте, Alexey F, Вы писали:
Тот пост, что писал раньше, удалил, теперь по-нормальному.

Делал по предложенному мануалу.
В результате получил длл-ки qsqliteicud4.dll и qsqliteicu4.dll, положил их в папку %QTDIR%\plugins\sqldrivers
Пытаюсь делать QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITEICU");
В ответ получаю:
QSqlDatabase: QSQLITEICU driver not loaded
QSqlDatabase: available drivers: QSQLITE QMYSQL3 QMYSQL

Подскажите пожалуйста, в чем я не прав?
Re[3]: Нормальное решение
От: Alexey F  
Дата: 14.10.11 10:18
Оценка:
Здравствуйте, Аноним, Вы писали:

А>QSqlDatabase: QSQLITEICU driver not loaded

А>QSqlDatabase: available drivers: QSQLITE QMYSQL3 QMYSQL

А>Подскажите пожалуйста, в чем я не прав?

DLL, от которых зависят эти две — т.е. целая пачка icu* лежат в %QTDIR%\plugins\sqldrivers?
Если попробовать загрузить DLL хоть через OllyDBG, она грузится или с исключением падает при загрузке?
Уже не помню, влияет ли это, но — компилятор тот же, которым собирался сам Qt?
Re[4]: Нормальное решение
От: WindAndRain Россия  
Дата: 14.10.11 12:25
Оценка:
Здравствуйте, Alexey F, Вы писали:

AF>Здравствуйте, Аноним, Вы писали:


А>>QSqlDatabase: QSQLITEICU driver not loaded

А>>QSqlDatabase: available drivers: QSQLITE QMYSQL3 QMYSQL

А>>Подскажите пожалуйста, в чем я не прав?

AF>DLL, от которых зависят эти две — т.е. целая пачка icu* лежат в %QTDIR%\plugins\sqldrivers?
AF>Если попробовать загрузить DLL хоть через OllyDBG, она грузится или с исключением падает при загрузке?
AF>Уже не помню, влияет ли это, но — компилятор тот же, которым собирался сам Qt?

dll-ли лежат.
Qt не собирался, устанавливал QtSDK.
Компилятор mingw.
OllyDBG при попытке открыть qsqliteicu4.dll говорит что не найдена точка входа в qtcore.dll, т.е. сама dll-ка вроде бы грузится.
Re[5]: Нормальное решение
От: Alexey F  
Дата: 14.10.11 12:49
Оценка:
Здравствуйте, WindAndRain, Вы писали:

WAR>OllyDBG при попытке открыть qsqliteicu4.dll говорит что не найдена точка входа в qtcore.dll, т.е. сама dll-ка вроде бы грузится.

Точнее наоборот.
OllyDBG подцепите к процессу, поставьте break на LoadLibraryW и посмотрите, прогружается она хоть или нет (код возврата LoadLibraryW + GetLastError; или он вообще плагин не видит и грузить не пытается).
Кстати, sqlbrowser тоже о новой sqlite не упоминает?
Свою версию уже давно не обновлял, смогу глубже копнуть когда она скачается/установится.
Re[6]: Нормальное решение
От: WindAndRain Россия  
Дата: 14.10.11 14:13
Оценка:
Здравствуйте, Alexey F, Вы писали:

AF>Здравствуйте, WindAndRain, Вы писали:


WAR>>OllyDBG при попытке открыть qsqliteicu4.dll говорит что не найдена точка входа в qtcore.dll, т.е. сама dll-ка вроде бы грузится.

AF>Точнее наоборот.
AF>OllyDBG подцепите к процессу, поставьте break на LoadLibraryW и посмотрите, прогружается она хоть или нет (код возврата LoadLibraryW + GetLastError; или он вообще плагин не видит и грузить не пытается).
AF>Кстати, sqlbrowser тоже о новой sqlite не упоминает?
AF>Свою версию уже давно не обновлял, смогу глубже копнуть когда она скачается/установится.

Собираю дистрибутив программы, кладу рядом с ней все нужные dll-ки, все запускается, like работает как надо, т.е. драйвер подцепился.
Из QtCreator программа не запускается, ругается на невалидный драйвер.

upd: положил icu* в папку куда делается сборка, программа запустилась. Видимо QTCreator не смог подхватить зависимости qsqliteicu из папки %QtDir%/plugins/sqldrivers

Проблема решена, спасибо!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.