[gelöst] Abspeichern SQLITE :memory: nach *.db

Alles rund um die Programmierung mit Qt
Antworten
jadokar
Beiträge: 5
Registriert: 26. März 2009 10:04
Kontaktdaten:

[gelöst] Abspeichern SQLITE :memory: nach *.db

Beitrag von jadokar »

Hi,

ich arbeite an einem Programm, welches Daten, mit einer gecachten SQLITE Datenbank (:memory:) im Hintergrund, verarbeitet. Ziel ist, diese Datenbank als Datei z.B. [NAME].db abzuspeichern, um später erneut darauf zugreifen zu können.

Code: Alles auswählen

QSqlDatabase db;    
db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName(":memory:");

if (!db.open()){
...
}
Punkte vorweg:
- Ja, ich könnte von Anfang an auf die Festplatte schreiben! Der Performance zu liebe und da nicht unbedingt fest steht ob die Datenbank gespeichert wird, wird per default eine inMemory Datenbank erzeugt
- Meine bisherigen Überlegungen waren, die Daten einzeln um zu schaufeln :?
quasi eine zweite Datenbankinstanz auf der Festplatte zu erzeugen mittels:

Code: Alles auswählen

QSqlDatabase t_db;    
t_db = QSqlDatabase::addDatabase("QSQLITE");
t_db.setDatabaseName("/my/path/file.db");

if (!t_db.open()){
...
}
und dann die Daten von db nach t_db schaufeln, indem alle Tabellen mittels QSqlQuery oder QSqlQueryModel abgefragt und tranferiert werden.

Daher meine Frage: Gibt es einen alternativen/besseren Weg, die Daten schneller/leichter abspeichern zu können bzw. hab ich etwas bei Qt übersehen das mir die Arbeit erspart?

Danke im Vorraus
jadokar
Zuletzt geändert von jadokar am 17. April 2009 12:29, insgesamt 1-mal geändert.
softwaremaker
Beiträge: 149
Registriert: 1. April 2009 19:25

schaue hier

Beitrag von softwaremaker »

jadokar
Beiträge: 5
Registriert: 26. März 2009 10:04
Kontaktdaten:

Beitrag von jadokar »

cool danke :D

hmmh Sqlite Doku so einfach... :oops:
Hätte ich mich mal nicht so an Qt fest gebissen...
jadokar
Beiträge: 5
Registriert: 26. März 2009 10:04
Kontaktdaten:

Beitrag von jadokar »

soweit... so gut...

Die angegebene "Loading and Saving In-Memory Databases" Methode aus der SQLite API habe ich in meine Klasse mit implementiert. Zusätzlich habe ich noch eine Speichermethode darüber gesetzt, welche wie folgt aussieht:

Code: Alles auswählen

bool database::saveDatabase(const QString &absoluteFilename)
{
    QVariant db_handle = db.driver()->handle();       //hole low-level database handler

    if(db_handle.isValid() && qstrcmp(db_handle.typeName(), "sqlite3")){
        sqlite3 *handle = *static_cast<sqlite3 **>(db_handle.data());
        if(handle != 0){
            int result = loadOrSaveDb(handle, absoluteFilename.toStdString().c_str(), 1);
            qDebug() << "SQLite return : " << result;         //result bisher immer == 6
            if(result != 0){
                qFatal("database::savaDatabase - Could not save Database");
                return false;
            }
        }
        else{
            qDebug() << "database handle is 0";
            return false;
        }
    }
    else{
        qDebug() << "wrong database handle (sqlite3)";
        return false;
    }

    return true;
}
Bisher funktioniert auch alles... Nur dass die abgespeicherte Datenbank [NAME].db 0 Byte groß ist und die loadOrSaveDb() Methode als Rückgabewert 6 statt 0 liefert, was laut SQLite Error Code ein SQLITE_LOCKED (A table in the database is locked) bedeutet.
Bis jetzt bin ich überfragt, woher das Problem kommt, da bis auf die Datenbankinstanz selbst von Qt keine weiteren Verbindungen zu irgendwelchen Tabellen gehen bzw. ein Schreibvorgang auf diese ausgeführt werden. Das LOCKED muss also irgendwo anders stecken.
Über weitere Hinweise wäre ich sehr dankbar, da SQLite komplettes Neuland ist :?
jadokar
Beiträge: 5
Registriert: 26. März 2009 10:04
Kontaktdaten:

Beitrag von jadokar »

Hallo,

da es leider doch keine weiteren Ideen bzw. Alternativen gibt, dachte ich mir, ich poste meine bisherige Erfahrung, damit der Beitrag nicht offen bleibt.

Zum prüfen wo der Fehler liegt, habe ich per Debugger mal die sqlite3 Datenstruktur untersucht, indem ich eine Prüfmethode mit in meine Datenbankklasse eingebaut habe. Diese Methode rufe ich immer dann auf wenn etwas mit der Sqlite Datenbank geschieht. Im Anhang habe ich mal die für mich interessantesten Resultate (wenn sich noch wer dafür interessiert bzw. bei diesem Problem steht :wink: ) mit dran gehangen.

Code: Alles auswählen

void database::checkSqliteHandle()
{
    sqlite3 *qt_sqlitedb;

    QVariant db_handle = db.driver()->handle();

    if(db_handle.isValid() && qstrcmp(db_handle.typeName(), "sqlite3")){
        qt_sqlitedb = *static_cast<sqlite3 **>(db_handle.data());
        if(qt_sqlitedb != 0){
            qDebug() << "BREAKPOINT";
        }
    }
}
Da ich beim 'kopieren' der Datenbank von :memory: nach [NAME].db mit der 'int loadOrSaveDb(sqlite3 *pInMemory, const char *zFilename, int isSave)' Methode immer ein Rückgabewert von 6 bekomme, dieser ErrorCode beim Groben überblicken/debuggen der nativen sqlite3 Funktionen gesetzt wird, sollte die Variable 'inTransaction' (siehe Debug Struktur) nicht einen vorgegebenen Wert entsprechen UND Qt nach dem ausführen eines QSqlQuery die Datenbank immer mit 'inTransaction = 230' sowie 'nTransaction = 103' stehen lässt; vermute ich einmal dass Qt die Transaktion nicht richtig abschliesst, oder dass es gewollt ist, nicht mit anderen Funktionen in der Datenbank herum zu spielen.
Ein Blick in die Qt Dokumentation zeigt mir, dass es bei QSqlQuery noch eine Methode 'void finish()' gibt mit der Anmerkung "This function is under development and is subject to change." Leider hat ein definieren des Makros QT_44_API_QSQLQUERY_FINISH und ein benutzen dieser Funktion keine Änderung gebracht.

Sollte ich weiterhin kein Erfolg haben, wäre ich wieder bei meiner ersten Idee oder ich verzichte auf die Qt Sql Funktionen und hole/erstelle mir einen Sqlite Wrapper auf c++ Basis der die nativen sqlite3 Funktionen verwendet.

so long
jadokar
Dateianhänge
debug_pointer.zip
Änderungen Sqlite3 Datenstruktur während des Arbeiten mit den Qt Sql Funktionen
(114.86 KiB) 175-mal heruntergeladen
jadokar
Beiträge: 5
Registriert: 26. März 2009 10:04
Kontaktdaten:

Beitrag von jadokar »

na also... 8)

wie schon befürchtet klappt es natürlich, nimmt man statt den Qt Sql Funktionen einen anderen Sqlite Wrapper für die Sqlite Datenbank. :)

Habe mir auf die Schnelle zum Testen diesen hier geborgt:
http://www.adp-gmbh.ch/sqlite/wrapper.html

Und siehe da, die Datenbank wurde wie gewollt von inMemory in ein *.db bzw. *.sqlite Binärfile gespeichert.
Ob das gesetzte 'inTransaction' Flag bei Qt jetzt ein Bug oder Feature ist ... gute Frage!

und danke nochmal an softwaremaker für den sqlite Tipp.
Antworten