Litkevich Yuriy |
Дата 12.12.2010, 17:51 |
|
Цитата(Frigolem @ 9.12.2010, 21:50)  Я чегой-то думал, что суровый будничный стек не уничтожит промежуточно создаваемый QByteArray ( его делает метод .toLocal8Bit() ) и я успею без проблем по-использовать указатель на строчку. Ан-нет, массив убивается уже походу следующих операций, наверно в результате оптимизации. это поведение стандартное. Т.к. QByteArray объект временный.
Также ожидай проблем и тут:
const char * modifiedFilename = someFunction(filename.toLocal8Bit().data());
Т.к. на следующей строке после этой функции временный объект умрёт |
Frigolem |
Дата 9.12.2010, 19:50 |
|
Бага обнаружилась. 
Вот так делать нельзя:
const char * zFilename = filename.toLocal8Bit().data(); Я чегой-то думал, что суровый будничный стек не уничтожит промежуточно создаваемый QByteArray ( его делает метод .toLocal8Bit() ) и я успею без проблем по-использовать указатель на строчку. Ан-нет, массив убивается уже походу следующих операций, наверно в результате оптимизации.
Поэтому, предлагаю в коде поста №2 сделать замену приведённой строки на эти две:
QByteArray array = filename.toLocal8Bit();
const char * zFilename = array.data();
В wiki также поправлю...  |
Frigolem |
Дата 30.11.2010, 18:28 |
|
Попробую написать заметки в раздел Qt/FAQ  |
igor_bogomolov |
Дата 30.11.2010, 17:07 |
|
Frigolem, может оформите заметку на нашу Wiki? |
Frigolem |
Дата 30.11.2010, 15:48 |
|
О! Получилось таки  Вот стоило спросить на форуме, как наконец докопался. 
Можно использовать SQLite API, идущий в комплекте с Qt. Оказалось, что никаких дополнительных библиотек делать не обязательно, достаточно просто приинклудить sqlite3.h, но при этом sqlite3.c соответственно нужно откомпилировать (оказывается, Qt без особых указаний на это его не откомпилирует).
Итак, действия примерно такие:
1. В .pro файл добавляем две строки:INCLUDEPATH = $$[QT_INSTALL_PREFIX]/src/3rdparty/sqlite
SOURCES += $$[QT_INSTALL_PREFIX]/src/3rdparty/sqlite/sqlite3.c
2. В файл, где собираемся использовать SQLite API, добавляем:#include <sqlite3.h>
3. После чего можно использовать примерно вот такую функцию для загрузки/сохранения SQLite БД из памяти / в память:Нормальная функция сохранения/загрузки БД SQLite
bool QOrg::sqliteDBMemFile( QSqlDatabase memdb, QString filename, bool save )
{
bool state = false;
QVariant v = memdb.driver()->handle();
if( v.isValid() && qstrcmp(v.typeName(),"sqlite3*") == 0 )
{
sqlite3 * handle = *static_cast<sqlite3 **>(v.data());
if( handle != 0 )
{
sqlite3 * pInMemory = handle;
const char * zFilename = filename.toLocal8Bit().data();
int rc;
sqlite3 *pFile;
sqlite3_backup *pBackup;
sqlite3 *pTo;
sqlite3 *pFrom;
rc = sqlite3_open( zFilename, &pFile );
if( rc==SQLITE_OK ){
pFrom = ( save ? pInMemory : pFile);
pTo = ( save ? pFile : pInMemory);
pBackup = sqlite3_backup_init(pTo, "main", pFrom, "main");
if( pBackup ){
(void)sqlite3_backup_step(pBackup, -1);
(void)sqlite3_backup_finish(pBackup);
}
rc = sqlite3_errcode(pTo);
}
(void)sqlite3_close(pFile);
if( rc == SQLITE_OK ) state = true;
}
}
return state;
}
Получилось и кроссплатформенно вроде, и надёжно. МангаджусЪ.  |
Frigolem |
Дата 30.11.2010, 12:28 |
|
Собственно, очень интересует, как по-нормальному реализовать сохранение/загрузку SQLite БД из памяти / в память.
Сейчас я покажу, как делать не надо. Примеры нехорошие, но рабочие.  Я их показываю только потому, что пока что иных рабочих способов не нашёл.
Нехороший рабочий пример записи SQLite БД из :memory: в файл bool writeDBMemory2File( QString filename )
{
QFile file( filename );
if( file.exists() ) file.remove();
QSqlDatabase srcdb = QSqlDatabase::database( "main_db" );
QSqlQuery srcQuery( srcdb ), srcSubQuery( srcdb );
if( !srcQuery.exec( QString("ATTACH DATABASE '%1' AS trgdb;").arg(filename) ) )
qDebug() << "[writeDBMemory2File]\n" << srcQuery.lastQuery() << "\n" << srcQuery.lastError().text();
if( !srcQuery.exec( "SELECT name FROM sqlite_master WHERE type = 'table';" ) )
qDebug() << "[writeDBMemory2File]\n" << srcQuery.lastQuery() << "\n" << srcQuery.lastError().text();
while( srcQuery.next() )
{
QString tableName = srcQuery.record().value( "name" ).toString();
if( tableName == "sqlite_sequence" ) continue;
if( !srcSubQuery.exec( QString("CREATE TABLE trgdb.'%1' AS SELECT * FROM '%1';").arg(tableName) ) )
qDebug() << "[writeDBMemory2File]\n" << srcSubQuery.lastQuery() << "\n" << srcSubQuery.lastError().text();
}
if( !srcQuery.exec( "CREATE TABLE trgdb.___tmp ( id INTEGER PRIMARY KEY AUTOINCREMENT, value INTEGER );" ) )
qDebug() << "[writeDBMemory2File]\n" << srcQuery.lastQuery() << "\n" << srcQuery.lastError().text();
if( !srcQuery.exec( "INSERT INTO trgdb.___tmp( value ) VALUES( 1 );" ) )
qDebug() << "[writeDBMemory2File]\n" << srcQuery.lastQuery() << "\n" << srcQuery.lastError().text();
if( !srcQuery.exec( "DROP TABLE trgdb.___tmp;" ) )
qDebug() << "[writeDBMemory2File]\n" << srcQuery.lastQuery() << "\n" << srcQuery.lastError().text();
if( !srcQuery.exec( "SELECT name, seq FROM sqlite_sequence;" ) )
qDebug() << "[writeDBMemory2File]\n" << srcQuery.lastQuery() << "\n" << srcQuery.lastError().text();
while( srcQuery.next() )
{
QString sName = srcQuery.record().value( "name" ).toString();
int sSeq = srcQuery.record().value( "seq" ).toInt();
if( !srcSubQuery.exec( QString("INSERT INTO trgdb.sqlite_sequence(name, seq) VALUES( '%1', %2 );").arg(sName).arg(sSeq) ) )
qDebug() << "[writeDBMemory2File]\n" << srcSubQuery.lastQuery() << "\n" << srcSubQuery.lastError().text();
}
if( !srcQuery.exec( "DETACH trgdb;" ) )
qDebug() << "[writeDBMemory2File]\n" << srcQuery.lastQuery() << "\n" << srcQuery.lastError().text();
QSqlDatabase::addDatabase( "QSQLITE", "file_db" );
{
QSqlDatabase trgdb = QSqlDatabase::database( "file_db", false );
if( trgdb.open() )
{
QSqlQuery trgQuery( trgdb );
if( !srcQuery.exec( "SELECT sql FROM sqlite_master WHERE type = 'index';" ) )
qDebug() << "[writeDBMemory2File]\n" << srcQuery.lastQuery() << "\n" << srcQuery.lastError().text();
while( srcQuery.next() )
{
if( !trgQuery.exec( QString("%1;").arg(srcQuery.record().value( "sql" ).toString()) ) )
qDebug() << "[writeDBMemory2File]\n" << trgQuery.lastQuery() << "\n" << trgQuery.lastError().text();
}
}
trgdb.close();
}
QSqlDatabase::removeDatabase( "file_db" );
return true;
}
Нехороший рабочий пример чтения SQLite БД из файла в :memory: bool readDBFile2Memory( QString filename )
{
QFile file( filename );
if( !file.exists() ) return false;
QSqlDatabase srcdb = QSqlDatabase::database( "main_db" );
srcdb.close();
srcdb.open();
QSqlQuery srcQuery( srcdb ), srcSubQuery( srcdb );
if( !srcQuery.exec( QString("ATTACH DATABASE '%1' AS trgdb;").arg(filename) ) )
qDebug() << "[readDBFile2Memory]\n" << srcQuery.lastQuery() << "\n" << srcQuery.lastError().text();
if( !srcQuery.exec( "SELECT name FROM trgdb.sqlite_master WHERE type = 'table';" ) )
qDebug() << "[readDBFile2Memory]\n" << srcQuery.lastQuery() << "\n" << srcQuery.lastError().text();
while( srcQuery.next() )
{
QString tableName = srcQuery.record().value( "name" ).toString();
if( tableName == "sqlite_sequence" ) continue;
if( !srcSubQuery.exec( QString("CREATE TABLE '%1' AS SELECT * FROM trgdb.'%1';").arg(tableName) ) )
qDebug() << "[readDBFile2Memory]\n" << srcSubQuery.lastQuery() << "\n" << srcSubQuery.lastError().text();
}
if( !srcQuery.exec( "CREATE TABLE ___tmp ( id INTEGER PRIMARY KEY AUTOINCREMENT, value INTEGER );" ) )
qDebug() << "[readDBFile2Memory]\n" << srcQuery.lastQuery() << "\n" << srcQuery.lastError().text();
if( !srcQuery.exec( "INSERT INTO ___tmp( value ) VALUES( 1 );" ) )
qDebug() << "[readDBFile2Memory]\n" << srcQuery.lastQuery() << "\n" << srcQuery.lastError().text();
if( !srcQuery.exec( "DROP TABLE ___tmp;" ) )
qDebug() << "[readDBFile2Memory]\n" << srcQuery.lastQuery() << "\n" << srcQuery.lastError().text();
if( !srcQuery.exec( "SELECT name, seq FROM trgdb.sqlite_sequence;" ) )
qDebug() << "[readDBFile2Memory]\n" << srcQuery.lastQuery() << "\n" << srcQuery.lastError().text();
while( srcQuery.next() )
{
QString sName = srcQuery.record().value( "name" ).toString();
int sSeq = srcQuery.record().value( "seq" ).toInt();
if( !srcSubQuery.exec( QString("INSERT INTO sqlite_sequence(name, seq) VALUES( '%1', %2 );").arg(sName).arg(sSeq) ) )
qDebug() << "[readDBFile2Memory]\n" << srcSubQuery.lastQuery() << "\n" << srcSubQuery.lastError().text();
}
if( !srcQuery.exec( "SELECT sql FROM trgdb.sqlite_master WHERE type = 'index';" ) )
qDebug() << "[readDBFile2Memory]\n" << srcQuery.lastQuery() << "\n" << srcQuery.lastError().text();
while( srcQuery.next() )
{
if( !srcSubQuery.exec( QString("%1;").arg(srcQuery.record().value( "sql" ).toString()) ) )
qDebug() << "[readDBFile2Memory]\n" << srcSubQuery.lastQuery() << "\n" << srcSubQuery.lastError().text();
}
if( !srcQuery.exec( "DETACH trgdb;" ) )
qDebug() << "[readDBFile2Memory]\n" << srcQuery.lastQuery() << "\n" << srcQuery.lastError().text();
return true;
}
Так делать в первую очередь не хорошо, потому что лично мне не известно, гарантированно ли вся БД перенесётся (достаточно ли только таблиц, индексов и sqlite_sequence). Во-вторых, если записей много, то операция получается вовсе не быстрой, так как делается множество малополезных в данной ситуации построчных операций.
Теперь о том, как было бы неплохо реализовать по-нормальному. 
На сайте SQLite есть статья, где рассказывается о том, что вообще для сохранения БД из памяти в файл и наоборот используется механизм резервирования. Приведены даже примеры как. Но, как становится понятно из статьи, делается это через SQLite API.
В Qt для таких случаев, вроде бы, предусмотрена возможность получить хэндлер API-шного подключения: QSqlDriver->Handle(); Есть в справке даже пример получения такого хэндлера. QSqlDatabase db = ...;
QVariant v = db.driver()->handle();
if (v.isValid() && qstrcmp(v.typeName(), "sqlite3*")==0) {
sqlite3 *handle = *static_cast<sqlite3 **>(v.data());
if (handle != 0) {
...
}
}
Но вот только для того, чтобы такой код откомпилировался, нужно подключить библиотеку SQLite. А вот как это сделать, чего-то мне найти не удалося.  Я нашёл ещё одну возможно полезную в данной ситуации статью. Там как раз вопрос про работу с SQLite API, и там у автора темы даже что-то вроде бы получилось. Автор говорит, что ему помогло вот так:Цитата("Автор говорит") The problem is solved by adding the following line to the .pro file
LIBS += -L/usr/lib -lsqlite3
and added #include "sqlite3.h" Судя по "/usr/lib" это, скорее всего, вариант для Linux. Также, скорее всего, SQLite там был установлен отдельно (коли в /usr/lib).
Собственно вопрос  Господа, не подскажете, а как можно подключить SQLite библиотеку под Windows? И обязательно ли это должна быть отдельная библиотека (а не из комплекта Qt)? |
Просмотр темы полностью (откроется в новом окне) |
|