defuze.me  Client
dbcore.cpp
00001 /**************************************************************************
00002 ** defuze.me Epitech Innovative Project
00003 **
00004 ** Copyright 2010
00005 **   Athena Calmettes - Jocelyn De La Rosa - Francois Gaillard
00006 **   Adrien Jarthon - Alexandre Moore - Luc Peres - Arnaud Sellier
00007 **
00008 ** All rights reserved.
00009 **************************************************************************/
00010 
00011 #include <iostream>
00012 #include <QDir>
00013 #include "dbcore.hpp"
00014 #include "exception.hpp"
00015 #include "parser.hpp"
00016 #include "status.hpp"
00017 
00018 using namespace DB;
00019 
00020 DBCore::DBCore(QStringList &)
00021 {
00022 }
00023 
00024 DBCore::~DBCore()
00025 {
00026     while (!locations.empty())
00027     {
00028         delete locations.back();
00029         locations.pop_back();
00030     }
00031 }
00032 
00033 void    DBCore::defineParams()
00034 {
00035     // Default locations
00036     QVariantList list;
00037     list.push_back(QDesktopServices::storageLocation(QDesktopServices::DataLocation) + "/database.db");
00038     list.push_back(QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation) + "/defuze.me/backup/database.db");
00039     setParameter("locations", list);
00040 }
00041 
00042 void    DBCore::loadLocations()
00043 {
00044     QVariantList list = getParameter("locations").toList();
00045     for (int i = 0; i < list.size(); i++)
00046         locations.push_back(new Location(list.at(i).toString()));
00047     currentLocation = NULL;
00048 }
00049 
00050 void    DBCore::saveLocations()
00051 {
00052     QVariantList list;
00053     for (int i = 0; i < locations.size(); i++)
00054         list.push_back(locations[i]->filePath());
00055     setParameter("locations", list);
00056     commitParameters();
00057 }
00058 
00059 void DBCore::init(Cores *c)
00060 {
00061     cores = c;
00062 
00063     setParamsName("db");
00064     setParamsBackEnd(Params::Parameterizable::SETTINGS);
00065     registerToParamsCore(cores->params());
00066     loadLocations();
00067     // Find the better location
00068     Location* original = locations.first();
00069 
00070     if (!original->exists() || !openLocation(original))
00071     {
00072         // WARNING: can't open original database !!!!!
00073 
00074         // try to load backups
00075         bool    backupOk = false;
00076         for(int i = 1; i < locations.size(); ++i)
00077         {
00078             if (openLocation(locations.at(i)))
00079             {
00080                 backupOk = true;
00081                 break;
00082             }
00083         }
00084         if (!backupOk && !openLocation(original))
00085             throw_exception(0x01, "No database found, no backup available, can't create a new one, please check your persmissions!");
00086     }
00087 
00088     MigrationEngine mEngine(*this);
00089     mEngine.migrate();
00090 
00091     if (getParameter("reset", false) == true)
00092     {
00093         qDebug() << "DB reset requested";
00094         // Undo all migrations
00095         mEngine.undo();
00096         // Migrate database to new schema
00097         mEngine.migrate();
00098     }
00099     // self-test
00100     //test();
00101 
00102     backupAll();
00103 }
00104 
00105 void DBCore::aboutToQuit()
00106 {
00107     connection.close();
00108     currentLocation = NULL;
00109 }
00110 
00111 bool    DBCore::openLocation(Location* loc)
00112 {
00113     qDebug() << "DB::Core opening database:" << loc->filePath();
00114     loc->mkPath();
00115     connection = QSqlDatabase::addDatabase("QSQLITE");
00116     connection.setDatabaseName(loc->filePath());
00117     connection.setUserName("defuze.me");
00118     if (!connection.open())
00119     {
00120         std::cerr << connection.lastError().text().toStdString() << std::endl;
00121         return false;
00122     }
00123     loc->open() = true;
00124     currentLocation = loc;
00125     return true;
00126 }
00127 
00128 QVariant    DBCore::setting(const QString& key) const
00129 {
00130     QSqlQuery query;
00131     QVariant value;
00132     query.prepare("SELECT * FROM settings WHERE key = :key LIMIT 1");
00133     query.bindValue(":key", key);
00134     query.exec();
00135     int fieldNo = query.record().indexOf("value");
00136     if (query.next())
00137         value = query.value(fieldNo);
00138     return (Network::JsonParser().parse(value.toByteArray()));
00139 }
00140 
00141 QHash<QString, QVariant> DBCore::settingsStartingWith(const QString &key) const
00142 {
00143     QHash<QString, QVariant> result;
00144     QSqlQuery query;
00145     QVariant value;
00146     QString fullKey;
00147     query.prepare("SELECT * FROM settings WHERE key LIKE :key");
00148     query.bindValue(":key", key + "%");
00149     query.exec();
00150     int keyFieldNo = query.record().indexOf("key");
00151     int valueFieldNo = query.record().indexOf("value");
00152     while (query.next())
00153     {
00154         fullKey = query.value(keyFieldNo).toString();
00155         value = query.value(valueFieldNo);
00156         result[fullKey] = Network::JsonParser().parse(value.toByteArray());
00157     }
00158     return (result);
00159 }
00160 
00161 bool    DBCore::setSetting(const QString& key, const QVariant value) const
00162 {
00163     QSqlQuery query;
00164     query.prepare("INSERT OR REPLACE INTO settings (key, value) VALUES (:key, :value)");
00165     query.bindValue(":key", key);
00166     query.bindValue(":value", Network::JsonParser().serialize(value));
00167     if (!query.exec())
00168         throw_exception(0x02, QString("Can't store setting value: " + query.lastError().text()));
00169     return true;
00170 }
00171 
00172 int     DBCore::currentMigration() const
00173 {
00174     QVariant    value = setting("migration");
00175     if (value.isNull())
00176         return 0;
00177     else
00178         return value.toInt();
00179 }
00180 
00181 void        DBCore::backupAll()
00182 {
00183     for(int i = 0; i < locations.size(); ++i)
00184     {
00185         Location* loc = locations.at(i);
00186         if (!loc->open())
00187         {
00188             qDebug() << "DB::Core backuping database to" << loc->filePath() << "(age=" << loc->age() << ")";
00189             loc->mkPath();
00190             QFile::remove(loc->filePath());
00191             QFile   database(currentLocation->filePath());
00192             if (!database.copy(loc->filePath()))
00193             {
00194                 Notification::Status::gMessage(tr("Can't backup to %1 (%2)").arg(loc->filePath()).arg(database.errorString()), Notification::WARN);
00195             }
00196             loc->reload();
00197         }
00198     }
00199 }
00200 
00201 void        DBCore::test()
00202 {
00203     // migration tests
00204     MigrationEngine mEngine(*this);
00205     mEngine.undo();
00206     mEngine.migrate();
00207     mEngine.undo();
00208     mEngine.migrate();
00209 
00210     // settings tests
00211     setSetting("test-foo", 42);
00212     Q_ASSERT_X(setting("test-foo") == 42, "DBCore::test", "Integer setting error");
00213     setSetting("test-foo", 40.42);
00214     Q_ASSERT_X(setting("test-foo") == 40.42, "DBCore::test", "Double setting error");
00215     setSetting("test-foo", "Hello");
00216     Q_ASSERT_X(setting("test-foo") == "Hello", "DBCore::test", "String setting error");
00217     QVariantList list;
00218     list.append(42);
00219     list.append(40.42);
00220     list.append("Hello");
00221     setSetting("test-foo", list);
00222     Q_ASSERT_X(setting("test-foo") == list, "DBCore::test", "Array setting error");
00223     QVariantMap map;
00224     map["int"] = 42;
00225     map["double"] = 40.42;
00226     map["string"] = "Hello";
00227     setSetting("test-foo", map);
00228     Q_ASSERT_X(setting("test-foo") == map, "DBCore::test", "Map setting error");
00229     Q_ASSERT_X(setting("test-empty") == QVariant(), "DBCore::test", "Empty setting error");
00230 }