/* * Copyright (C) 2012-2013 Team XBMC * http://xbmc.org * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with XBMC; see the file COPYING. If not, see * . * */ #include "DatabaseManager.h" #include "utils/log.h" #include "addons/AddonDatabase.h" #include "view/ViewDatabase.h" #include "TextureDatabase.h" #include "music/MusicDatabase.h" #include "video/VideoDatabase.h" #include "pvr/PVRDatabase.h" #include "epg/EpgDatabase.h" #include "settings/AdvancedSettings.h" #include "cores/AudioEngine/Engines/ActiveAE/AudioDSPAddons/ActiveAEDSP.h" using namespace EPG; using namespace PVR; using namespace ActiveAE; CDatabaseManager &CDatabaseManager::GetInstance() { static CDatabaseManager s_manager; return s_manager; } CDatabaseManager::CDatabaseManager(): m_bIsUpgrading(false) { } CDatabaseManager::~CDatabaseManager() { } void CDatabaseManager::Initialize(bool addonsOnly) { Deinitialize(); { CAddonDatabase db; UpdateDatabase(db); } if (addonsOnly) return; CLog::Log(LOGDEBUG, "%s, updating databases...", __FUNCTION__); // NOTE: Order here is important. In particular, CTextureDatabase has to be updated // before CVideoDatabase. { CViewDatabase db; UpdateDatabase(db); } { CTextureDatabase db; UpdateDatabase(db); } { CMusicDatabase db; UpdateDatabase(db, &g_advancedSettings.m_databaseMusic); } { CVideoDatabase db; UpdateDatabase(db, &g_advancedSettings.m_databaseVideo); } { CPVRDatabase db; UpdateDatabase(db, &g_advancedSettings.m_databaseTV); } { CEpgDatabase db; UpdateDatabase(db, &g_advancedSettings.m_databaseEpg); } { CActiveAEDSPDatabase db; UpdateDatabase(db, &g_advancedSettings.m_databaseADSP); } CLog::Log(LOGDEBUG, "%s, updating databases... DONE", __FUNCTION__); m_bIsUpgrading = false; } void CDatabaseManager::Deinitialize() { CSingleLock lock(m_section); m_dbStatus.clear(); } bool CDatabaseManager::CanOpen(const std::string &name) { CSingleLock lock(m_section); std::map::const_iterator i = m_dbStatus.find(name); if (i != m_dbStatus.end()) return i->second == DB_READY; return false; // db isn't even attempted to update yet } void CDatabaseManager::UpdateDatabase(CDatabase &db, DatabaseSettings *settings) { std::string name = db.GetBaseDBName(); UpdateStatus(name, DB_UPDATING); if (Update(db, settings ? *settings : DatabaseSettings())) UpdateStatus(name, DB_READY); else UpdateStatus(name, DB_FAILED); } bool CDatabaseManager::Update(CDatabase &db, const DatabaseSettings &settings) { DatabaseSettings dbSettings = settings; db.InitSettings(dbSettings); int version = db.GetSchemaVersion(); std::string latestDb = dbSettings.name; latestDb += StringUtils::Format("%d", version); while (version >= db.GetMinSchemaVersion()) { std::string dbName = dbSettings.name; if (version) dbName += StringUtils::Format("%d", version); if (db.Connect(dbName, dbSettings, false)) { // Database exists, take a copy for our current version (if needed) and reopen that one if (version < db.GetSchemaVersion()) { CLog::Log(LOGNOTICE, "Old database found - updating from version %i to %i", version, db.GetSchemaVersion()); m_bIsUpgrading = true; bool copy_fail = false; try { db.CopyDB(latestDb); } catch (...) { CLog::Log(LOGERROR, "Unable to copy old database %s to new version %s", dbName.c_str(), latestDb.c_str()); copy_fail = true; } db.Close(); if (copy_fail) return false; if (!db.Connect(latestDb, dbSettings, false)) { CLog::Log(LOGERROR, "Unable to open freshly copied database %s", latestDb.c_str()); return false; } } // yay - we have a copy of our db, now do our worst with it if (UpdateVersion(db, latestDb)) return true; // update failed - loop around and see if we have another one available db.Close(); } // drop back to the previous version and try that version--; } // try creating a new one if (db.Connect(latestDb, dbSettings, true)) return true; // failed to update or open the database db.Close(); CLog::Log(LOGERROR, "Unable to create new database"); return false; } bool CDatabaseManager::UpdateVersion(CDatabase &db, const std::string &dbName) { int version = db.GetDBVersion(); bool bReturn = false; if (version < db.GetMinSchemaVersion()) { CLog::Log(LOGERROR, "Can't update database %s from version %i - it's too old", dbName.c_str(), version); return false; } else if (version < db.GetSchemaVersion()) { CLog::Log(LOGNOTICE, "Attempting to update the database %s from version %i to %i", dbName.c_str(), version, db.GetSchemaVersion()); bool success = true; db.BeginTransaction(); try { // drop old analytics, update table(s), recreate analytics, update version db.DropAnalytics(); db.UpdateTables(version); db.CreateAnalytics(); db.UpdateVersionNumber(); } catch (...) { CLog::Log(LOGERROR, "Exception updating database %s from version %i to %i", dbName.c_str(), version, db.GetSchemaVersion()); success = false; } if (!success) { CLog::Log(LOGERROR, "Error updating database %s from version %i to %i", dbName.c_str(), version, db.GetSchemaVersion()); db.RollbackTransaction(); return false; } bReturn = db.CommitTransaction(); CLog::Log(LOGINFO, "Update to version %i successful", db.GetSchemaVersion()); } else if (version > db.GetSchemaVersion()) { bReturn = false; CLog::Log(LOGERROR, "Can't open the database %s as it is a NEWER version than what we were expecting?", dbName.c_str()); } else { bReturn = true; CLog::Log(LOGNOTICE, "Running database version %s", dbName.c_str()); } return bReturn; } void CDatabaseManager::UpdateStatus(const std::string &name, DB_STATUS status) { CSingleLock lock(m_section); m_dbStatus[name] = status; }