Permalink
Browse files

Merge backup_on_update_library branch, allowing a separate .db file p…

…er database version, meaning you can revert to an earlier version of XBMC and still have your old database intact. Does not work with mysql.
  • Loading branch information...
2 parents 3df64e8 + d153d81 commit 0dcaf1fb822cb8cab3fed6a61118c20ac9b2c7e8 Jonathan Marshall committed Feb 26, 2011
View
@@ -61,5 +61,5 @@ class CTextureDatabase : public CDatabase
virtual bool CreateTables();
virtual bool UpdateOldVersion(int version);
virtual int GetMinVersion() const { return 6; };
- const char *GetDefaultDBName() const { return "Textures"; };
+ const char *GetBaseDBName() const { return "Textures"; };
};
View
@@ -38,5 +38,5 @@ class CViewDatabase : public CDatabase
virtual bool CreateTables();
virtual bool UpdateOldVersion(int version);
virtual int GetMinVersion() const { return 3; };
- const char *GetDefaultDBName() const { return "ViewModes"; };
+ const char *GetBaseDBName() const { return "ViewModes"; };
};
@@ -99,6 +99,6 @@ class CAddonDatabase : public CDatabase
virtual bool CreateTables();
virtual bool UpdateOldVersion(int version);
virtual int GetMinVersion() const { return 12; }
- const char *GetDefaultDBName() const { return "Addons"; }
+ const char *GetBaseDBName() const { return "Addons"; }
};
@@ -25,8 +25,10 @@
#include "settings/AdvancedSettings.h"
#include "utils/Crc32.h"
#include "filesystem/SpecialProtocol.h"
+#include "filesystem/File.h"
#include "utils/AutoPtrHandle.h"
#include "utils/log.h"
+#include "utils/URIUtils.h"
using namespace AUTOPTR;
using namespace dbiplus;
@@ -35,8 +37,7 @@ using namespace dbiplus;
CDatabase::CDatabase(void)
{
- m_bOpen = false;
- m_iRefCount = 0;
+ m_openCount = 0;
m_sqlite = true;
m_bMultiWrite = false;
}
@@ -253,32 +254,81 @@ bool CDatabase::Open(DatabaseSettings &dbSettings)
{
if (IsOpen())
{
- m_iRefCount++;
+ m_openCount++;
return true;
}
m_sqlite = true;
-
+
if ( dbSettings.type.Equals("mysql") )
{
// check we have all information before we cancel the fallback
- if ( ! (dbSettings.host.IsEmpty() || dbSettings.user.IsEmpty() || dbSettings.pass.IsEmpty()) )
+ if ( ! (dbSettings.host.IsEmpty() || dbSettings.name.IsEmpty() ||
+ dbSettings.user.IsEmpty() || dbSettings.pass.IsEmpty()) )
m_sqlite = false;
else
- CLog::Log(LOGINFO, "essential mysql database information is missing (eg. host, user, pass)");
+ CLog::Log(LOGINFO, "essential mysql database information is missing (eg. host, name, user, pass)");
}
- // set default database name if appropriate
- if ( dbSettings.name.IsEmpty() )
- dbSettings.name = GetDefaultDBName();
-
- // always safely fallback to sqlite3
+ // always safely fallback to sqlite3, and use separate, versioned database
if (m_sqlite)
{
dbSettings.type = "sqlite3";
dbSettings.host = _P(g_settings.GetDatabaseFolder());
+
+ int version = GetMinVersion();
+ CStdString latestDb;
+ latestDb.Format("%s%d.db", GetBaseDBName(), version);
+ while (version >= 0)
+ {
+ if (version)
+ dbSettings.name.Format("%s%d.db", GetBaseDBName(), version);
+ else
+ dbSettings.name.Format("%s.db", GetBaseDBName());
+ if (Connect(dbSettings, false))
+ {
+ // Database exists, take a copy for our current version (if needed) and reopen that one
+ if (version < GetMinVersion())
+ {
+ CLog::Log(LOGNOTICE, "Old database found - updating from version %i to %i", version, GetMinVersion());
+ Close();
+ CStdString currentDb = URIUtils::AddFileToFolder(dbSettings.host, dbSettings.name);
+ CStdString newPath = URIUtils::AddFileToFolder(dbSettings.host, latestDb);
+ if (!XFILE::CFile::Cache(currentDb, newPath))
+ {
+ CLog::Log(LOGERROR, "Unable to copy old database %s to new version %s", dbSettings.name.c_str(), latestDb.c_str());
+ return false;
+ }
+ dbSettings.name = latestDb;
+ if (!Connect(dbSettings, false))
+ {
+ CLog::Log(LOGERROR, "Unable to open freshly copied database %s", dbSettings.name.c_str());
+ return false;
+ }
+ }
+ // yay - we have a copy of our db, now do our worst with it
+ if (UpdateVersion(dbSettings.name))
+ return true;
+ // update failed - loop around and see if we have another one available
+ Close();
+ }
+ // drop back to the previous version and try that
+ version--;
+ }
+ // unable to open any version fall through to create a new one
+ dbSettings.name = latestDb;
}
+ if (Connect(dbSettings, true) && UpdateVersion(dbSettings.name))
+ return true;
+ // failed to update or open the database
+ Close();
+ CLog::Log(LOGERROR, "Unable to open database %s", dbSettings.name.c_str());
+ return false;
+}
+
+bool CDatabase::Connect(DatabaseSettings &dbSettings, bool create)
+{
// create the appropriate database structure
if (dbSettings.type.Equals("sqlite3"))
{
@@ -313,14 +363,11 @@ bool CDatabase::Open(DatabaseSettings &dbSettings)
m_pDS.reset(m_pDB->CreateDataset());
m_pDS2.reset(m_pDB->CreateDataset());
- if (m_pDB->connect() != DB_CONNECTION_OK)
- {
- CLog::Log(LOGERROR, "Unable to open database at host: %s db: %s (old version?)", dbSettings.host.c_str(), dbSettings.name.c_str());
+ if (m_pDB->connect(create) != DB_CONNECTION_OK)
return false;
- }
// test if db already exists, if not we need to create the tables
- if (!m_pDB->exists())
+ if (!m_pDB->exists() && create)
{
if (dbSettings.type.Equals("sqlite3"))
{
@@ -337,64 +384,61 @@ bool CDatabase::Open(DatabaseSettings &dbSettings)
CreateTables();
}
- // Mark our db as open here to make our destructor to properly close the file handle
- m_bOpen = true;
+ // sqlite3 post connection operations
+ if (dbSettings.type.Equals("sqlite3"))
+ {
+ m_pDS->exec("PRAGMA cache_size=4096\n");
+ m_pDS->exec("PRAGMA synchronous='NORMAL'\n");
+ m_pDS->exec("PRAGMA count_changes='OFF'\n");
+ }
+
+ m_openCount = 1; // our database is open
+ return true;
+}
- // Database exists, check the version number
+bool CDatabase::UpdateVersion(const CStdString &dbName)
+{
int version = 0;
m_pDS->query("SELECT idVersion FROM version\n");
if (m_pDS->num_rows() > 0)
version = m_pDS->fv("idVersion").get_asInt();
if (version < GetMinVersion())
{
- CLog::Log(LOGNOTICE, "Attempting to update the database %s from version %i to %i", dbSettings.name.c_str(), version, GetMinVersion());
+ CLog::Log(LOGNOTICE, "Attempting to update the database %s from version %i to %i", dbName.c_str(), version, GetMinVersion());
if (UpdateOldVersion(version) && UpdateVersionNumber())
CLog::Log(LOGINFO, "Update to version %i successfull", GetMinVersion());
else
{
- CLog::Log(LOGERROR, "Can't update the database %s from version %i to %i", dbSettings.name.c_str(), version, GetMinVersion());
- Close();
+ CLog::Log(LOGERROR, "Can't update the database %s from version %i to %i", dbName.c_str(), version, GetMinVersion());
return false;
}
}
else if (version > GetMinVersion())
{
- CLog::Log(LOGERROR, "Can't open the database %s as it is a NEWER version than what we were expecting!", dbSettings.name.c_str());
- Close();
+ CLog::Log(LOGERROR, "Can't open the database %s as it is a NEWER version than what we were expecting?", dbName.c_str());
return false;
}
-
- // sqlite3 post connection operations
- if (dbSettings.type.Equals("sqlite3"))
- {
- m_pDS->exec("PRAGMA cache_size=4096\n");
- m_pDS->exec("PRAGMA synchronous='NORMAL'\n");
- m_pDS->exec("PRAGMA count_changes='OFF'\n");
- }
-
- m_iRefCount++;
return true;
}
bool CDatabase::IsOpen()
{
- return m_bOpen;
+ return m_openCount > 0;
}
void CDatabase::Close()
{
- if (!m_bOpen)
- return ;
+ if (m_openCount == 0)
+ return;
- if (m_iRefCount > 1)
+ if (m_openCount > 1)
{
- m_iRefCount--;
- return ;
+ m_openCount--;
+ return;
}
- m_iRefCount--;
- m_bOpen = false;
+ m_openCount = 0;
if (NULL == m_pDB.get() ) return ;
if (NULL != m_pDS.get()) m_pDS->close();
@@ -112,18 +112,20 @@ class CDatabase
virtual bool UpdateOldVersion(int version) { return true; };
virtual int GetMinVersion() const=0;
- virtual const char *GetDefaultDBName() const=0;
+ virtual const char *GetBaseDBName() const=0;
+
+ bool UpdateVersion(const CStdString &dbName);
- bool m_bOpen;
bool m_sqlite; ///< \brief whether we use sqlite (defaults to true)
std::auto_ptr<dbiplus::Database> m_pDB;
std::auto_ptr<dbiplus::Dataset> m_pDS;
std::auto_ptr<dbiplus::Dataset> m_pDS2;
private:
+ bool Connect(DatabaseSettings &db, bool create);
bool UpdateVersionNumber();
bool m_bMultiWrite; /*!< True if there are any queries in the queue, false otherwise */
- int m_iRefCount;
+ unsigned int m_openCount;
};
@@ -60,7 +60,7 @@ int Database::connectFull(const char *newHost, const char *newPort, const char *
db = newDb;
login = newLogin;
passwd = newPasswd;
- return connect();
+ return connect(true);
}
@@ -115,7 +115,7 @@ class Database {
virtual int setErr(int err_code, const char *qry)=0;
virtual const char *getErrorMsg(void) { return error.c_str(); }
- virtual int connect(void) { return DB_COMMAND_OK; }
+ virtual int connect(bool create) { return DB_COMMAND_OK; }
virtual int connectFull( const char *newDb, const char *newHost=NULL,
const char *newLogin=NULL, const char *newPasswd=NULL,const char *newPort=NULL);
virtual void disconnect(void) { active = false; }
@@ -104,7 +104,7 @@ const char *MysqlDatabase::getErrorMsg() {
return error.c_str();
}
-int MysqlDatabase::connect() {
+int MysqlDatabase::connect(bool create_new) {
try
{
// don't reconnect if ping is ok
@@ -221,7 +221,7 @@ int MysqlDatabase::query_with_reconnect(const char* query) {
{
CLog::Log(LOGINFO,"MYSQL server has gone. Will try %d more attempt(s) to reconnect.", attempts);
active = false;
- connect();
+ connect(true);
}
// grab the latest error if not ok
@@ -299,7 +299,7 @@ void MysqlDatabase::rollback_transaction() {
bool MysqlDatabase::exists() {
// Uncorrect name, check if tables are present inside the db
- connect();
+ connect(true);
if (active && conn != NULL)
{
MYSQL_RES* res = mysql_list_dbs(conn, db.c_str());
@@ -57,7 +57,7 @@ class MysqlDatabase: public Database {
virtual const char *getErrorMsg();
/* func. connects to database-server */
- virtual int connect();
+ virtual int connect(bool create);
/* func. disconnects from database-server */
virtual void disconnect();
/* func. creates new database */
@@ -30,7 +30,6 @@
#include <string>
#include "sqlitedataset.h"
-#include "utils/log.h"
#include "system.h" // for Sleep(), OutputDebugString() and GetLastError()
using namespace std;
@@ -166,7 +165,7 @@ const char *SqliteDatabase::getErrorMsg() {
return error.c_str();
}
-int SqliteDatabase::connect() {
+int SqliteDatabase::connect(bool create) {
if (host.empty() || db.empty())
return DB_CONNECTION_NONE;
@@ -205,7 +204,10 @@ int SqliteDatabase::connect() {
{
disconnect();
- if (sqlite3_open(db_fullpath.c_str(), &conn)==SQLITE_OK)
+ int flags = SQLITE_OPEN_READWRITE;
+ if (create)
+ flags |= SQLITE_OPEN_CREATE;
+ if (sqlite3_open_v2(db_fullpath.c_str(), &conn, flags, NULL)==SQLITE_OK)
{
sqlite3_busy_handler(conn, busy_callback, NULL);
char* err=NULL;
@@ -217,13 +219,10 @@ int SqliteDatabase::connect() {
return DB_CONNECTION_OK;
}
- CLog::Log(LOGERROR, "Unable to open database: %s (%u)", db_fullpath.c_str(), GetLastError());
return DB_CONNECTION_NONE;
}
catch(...)
{
- CLog::Log(LOGERROR, "Unable to open database: %s (%u)",
- db_fullpath.c_str(), GetLastError());
}
return DB_CONNECTION_NONE;
}
@@ -253,7 +252,7 @@ void SqliteDatabase::disconnect(void) {
}
int SqliteDatabase::create() {
- return connect();
+ return connect(true);
}
int SqliteDatabase::drop() {
@@ -67,7 +67,7 @@ class SqliteDatabase: public Database {
virtual const char *getErrorMsg();
/* func. connects to database-server */
- virtual int connect();
+ virtual int connect(bool create);
/* func. disconnects from database-server */
virtual void disconnect();
/* func. creates new database */
@@ -215,7 +215,7 @@ class CMusicDatabase : public CDatabase
virtual bool CreateTables();
virtual int GetMinVersion() const { return 16; };
- const char *GetDefaultDBName() const { return "MyMusic7"; };
+ const char *GetBaseDBName() const { return "MyMusic"; };
int AddAlbum(const CStdString& strAlbum1, int idArtist, const CStdString &extraArtists, const CStdString &strArtist1, int idThumb, int idGenre, const CStdString &extraGenres, int year);
int AddGenre(const CStdString& strGenre);
@@ -66,7 +66,7 @@ class CProgramDatabase : public CDatabase
virtual bool CreateTables();
virtual bool UpdateOldVersion(int version);
virtual int GetMinVersion() const { return 3; };
- const char *GetDefaultDBName() const { return "MyPrograms6"; };
+ const char *GetBaseDBName() const { return "MyPrograms"; };
FILETIME TimeStampToLocalTime( uint64_t timeStamp );
};
Oops, something went wrong.

0 comments on commit 0dcaf1f

Please sign in to comment.