crossplatform.ru

Здравствуйте, гость ( Вход | Регистрация )

> SQLite БД из памяти в файл, из файла в память, А также вопрос подключения библиотеки SQLite
Frigolem
  опции профиля:
сообщение 30.11.2010, 12:28
Сообщение #1


Студент
*

Группа: Новичок
Сообщений: 14
Регистрация: 24.9.2008
Пользователь №: 297

Спасибо сказали: 3 раз(а)




Репутация:   0  


Собственно, очень интересует, как по-нормальному реализовать сохранение/загрузку SQLite БД из памяти / в память.

Сейчас я покажу, как делать не надо. :) Примеры нехорошие, но рабочие. :)
Я их показываю только потому, что пока что иных рабочих способов не нашёл.

Нехороший рабочий пример записи SQLite БД из :memory: в файл
bool writeDBMemory2File( QString filename )
{
    // Указанный для записи файл должен быть предварительно уничтожен
    QFile file( filename );
    if( file.exists() ) file.remove();

    // Берём подключение к исходной БД. Подразумевается, что БД уже открыта.
    // БД может быть расположена в :memory:.
    QSqlDatabase srcdb = QSqlDatabase::database( "main_db" );
    // Конструируем запросники для srcdb
    QSqlQuery srcQuery( srcdb ), srcSubQuery( srcdb );

    // Приаттачиваем БД файла к БД из памяти (Наоборот приаттачивать нельзя,
    // так как при приаттачивании или открытии :memory: создаётся новый пустой
    // блок памяти. Уже созданный :memory: приаттачить не получится).
    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();
        // Для таблицы 'sqlite_sequence' требуется особый подход
        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();
    }

    // Провоцируем создание таблицы sqlie_sequence, так как напрямую её
    // создавать нельзя.
    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" );
    {
        // Процесс работы с БД изолирован, так как в случае ошибки
        // removeDatabase требует деструкцию всех компонентов, относящихся
        // к удаляемому соединению
        QSqlDatabase trgdb = QSqlDatabase::database( "file_db", false );
        if( trgdb.open() )
        {
            // Конструируем запросник для новооткрытого соединения. Конструировать
            // запросники можно только для уже открытых соединений, иначе
            // будем получать ошибки типа "Out of memory".
            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;

    // Берём подключение к исходной БД. Подразумевается, что БД уже открыта.
    // БД может быть расположена в :memory:.
    QSqlDatabase srcdb = QSqlDatabase::database( "main_db" );
    // Очищаем БД в памяти
    srcdb.close();
    srcdb.open();

    // Конструируем запросники для srcdb (создавать их можно только после
    // операций открытия/переоткрытия БД)
    QSqlQuery srcQuery( srcdb ), srcSubQuery( srcdb );

    // Приаттачиваем БД файла к БД из памяти (Наоборот приаттачивать нельзя,
    // так как при приаттачивании или открытии :memory: создаётся новый пустой
    // блок памяти. Уже созданный :memory: приаттачить не получится).
    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();
        // Для таблицы 'sqlite_sequence' требуется особый подход
        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();
    }

    // Провоцируем создание таблицы sqlie_sequence, так как напрямую её
    // создавать нельзя.
    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) {
     // v.data() returns a pointer to the handle
     sqlite3 *handle = *static_cast<sqlite3 **>(v.data());
     if (handle != 0) { // check that it is not NULL
         ...
     }
}
Но вот только для того, чтобы такой код откомпилировался, нужно подключить библиотеку 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)?
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
 
Начать новую тему
Ответов
Frigolem
  опции профиля:
сообщение 30.11.2010, 18:28
Сообщение #2


Студент
*

Группа: Новичок
Сообщений: 14
Регистрация: 24.9.2008
Пользователь №: 297

Спасибо сказали: 3 раз(а)




Репутация:   0  


Попробую написать заметки в раздел Qt/FAQ :)
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение

Сообщений в этой теме


Быстрый ответОтветить в данную темуНачать новую тему
Теги
Нет тегов для показа


1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0




RSS Текстовая версия Сейчас: 27.12.2024, 19:09