#include "LogCache.hpp" #include #include #include #include #include #include "svnqt/path.hpp" #include "svnqt/cache/DatabaseException.hpp" #ifndef NO_SQLITE3 #include "sqlite3/qsql_sqlite3.h" #define SQLTYPE "QSQLITE3" #else #define SQLTYPE "QSQLITE" #endif #define SQLMAIN "logmain-logcache" #define SQLMAINTABLE "logdb" namespace svn { namespace cache { LogCache* LogCache::mSelf = 0; class ThreadDBStore { public: ThreadDBStore(){ m_DB=0; } ~ThreadDBStore(){ m_DB=0; TQSqlDatabase::removeDatabase(key); TQMap::Iterator it; for (it=reposCacheNames.begin();it!=reposCacheNames.end();++it) { TQSqlDatabase::removeDatabase(it.data()); } } TQDataBase m_DB; TQString key; TQMap reposCacheNames; }; class LogCacheData { protected: TQMutex m_singleDbMutex; public: LogCacheData(){} ~LogCacheData(){ if (m_mainDB.hasLocalData()) { m_mainDB.setLocalData(0L); } } bool checkReposDb(TQDataBase aDb) { if (!aDb) { return false; } if (!aDb->open()) { return false; } TQSqlQuery _q(TQString(), aDb); TQStringList list = aDb->tables(); if (list.find("logentries")==list.end()) { aDb->transaction(); _q.exec("CREATE TABLE \"logentries\" (\"revision\" INTEGER UNIQUE,\"date\" INTEGER,\"author\" TEXT, \"message\" TEXT)"); aDb->commit(); } if (list.find("changeditems")==list.end()) { aDb->transaction(); _q.exec("CREATE TABLE \"changeditems\" (\"revision\" INTEGER,\"changeditem\" TEXT,\"action\" TEXT,\"copyfrom\" TEXT,\"copyfromrev\" INTEGER, PRIMARY KEY(revision,changeditem,action))"); aDb->commit(); } list = aDb->tables(); if (list.find("logentries")==list.end() || list.find("changeditems")==list.end()) { return false; } return true; } TQString createReposDB(const svn::Path&reposroot) { TQMutexLocker locker( &m_singleDbMutex ); TQDataBase _mdb = getMainDB(); TQSqlQuery query1(TQString(),_mdb); TQString q("insert into "+TQString(SQLMAINTABLE)+" (reposroot) VALUES('"+reposroot+"')"); _mdb->transaction(); query1.exec(q); _mdb->commit(); TQSqlQuery query(TQString(),_mdb); query.prepare(s_reposSelect); query.bindValue(0,reposroot.native()); query.exec(); TQString db; if (query.lastError().type()==TQSqlError::None && query.next()) { db = query.value(0).toString(); } else { qDebug("Error select_01: %s (%s)",query.lastError().text().TOUTF8().data(), query.lastQuery().TOUTF8().data()); } if (!db.isEmpty()) { TQString fulldb = m_BasePath+"/"+db+".db"; TQDataBase _db = TQSqlDatabase::addDatabase(SQLTYPE,"tmpdb"); _db->setDatabaseName(fulldb); if (!checkReposDb(_db)) { } TQSqlDatabase::removeDatabase("tmpdb"); } return db; } TQDataBase getReposDB(const svn::Path&reposroot) { if (!getMainDB()) { return 0; } bool checkDone = false; // make sure path is correct eg. without traling slashes. TQString dbFile; TQSqlQuery c(TQString(),getMainDB()); c.prepare(s_reposSelect); c.bindValue(0,reposroot.native()); c.exec(); //qDebug("Check for path: "+reposroot.native()); // only the first one if ( c.next() ) { /* qDebug( c.value(0).toString() + ": " + c.value(0).toString() );*/ dbFile = c.value(0).toString(); } if (dbFile.isEmpty()) { dbFile = createReposDB(reposroot); if (dbFile.isEmpty()) { return 0; } checkDone=true; } if (m_mainDB.localData()->reposCacheNames.find(dbFile)!=m_mainDB.localData()->reposCacheNames.end()) { return TQSqlDatabase::database(m_mainDB.localData()->reposCacheNames[dbFile]); } int i = 0; TQString _key = dbFile; while (TQSqlDatabase::contains(_key)) { _key = TQString("%1-%2").arg(dbFile).arg(i++); } // qDebug("The repository key is now: %s",_key.TOUTF8().data()); TQDataBase _db = TQSqlDatabase::addDatabase(SQLTYPE,_key); if (!_db) { return 0; } TQString fulldb = m_BasePath+"/"+dbFile+".db"; _db->setDatabaseName(fulldb); // qDebug("try database open %s",fulldb.TOUTF8().data()); if (!checkReposDb(_db)) { qDebug("no DB opened"); _db = 0; } else { qDebug("Insert into map"); m_mainDB.localData()->reposCacheNames[dbFile]=_key; } return _db; } TQDataBase getMainDB()const { if (!m_mainDB.hasLocalData()) { unsigned i=0; TQString _key = SQLMAIN; while (TQSqlDatabase::contains(_key)) { _key.sprintf("%s-%i",SQLMAIN,i++); } qDebug("The key is now: %s",_key.TOUTF8().data()); TQDataBase db = TQSqlDatabase::addDatabase(SQLTYPE,_key); db->setDatabaseName(m_BasePath+"/maindb.db"); if (!db->open()) { qWarning("%s", TQString("Failed to open main database: " + db->lastError().text()).ascii()); } else { m_mainDB.setLocalData(new ThreadDBStore); m_mainDB.localData()->key = _key; m_mainDB.localData()->m_DB = db; } } if (m_mainDB.hasLocalData()) { return m_mainDB.localData()->m_DB; } else { return 0; } } TQString m_BasePath; mutable TQThreadStorage m_mainDB; static const TQString s_reposSelect; }; TQString LogCache::s_CACHE_FOLDER="logcache"; const TQString LogCacheData::s_reposSelect=TQString("SELECT id from ")+TQString(SQLMAINTABLE)+TQString(" where reposroot=? ORDER by id DESC"); /*! \fn svn::cache::LogCache::LogCache() */ LogCache::LogCache() { m_BasePath = TQDir::HOMEDIR()+"/.svnqt"; setupCachePath(); } LogCache::LogCache(const TQString&aBasePath) { if (mSelf) { delete mSelf; } mSelf=this; if (aBasePath.isEmpty()) { m_BasePath=TQDir::HOMEDIR()+"/.svnqt"; } else { m_BasePath=aBasePath; } setupCachePath(); } LogCache::~LogCache() { } /*! \fn svn::cache::LogCache::setupCachePath() */ void LogCache::setupCachePath() { m_CacheData = new LogCacheData; m_CacheData->m_BasePath=m_BasePath; TQDir d; if (!d.exists(m_BasePath)) { d.mkdir(m_BasePath); } m_BasePath=m_BasePath+"/"+s_CACHE_FOLDER; if (!d.exists(m_BasePath)) { d.mkdir(m_BasePath); } m_CacheData->m_BasePath=m_BasePath; if (d.exists(m_BasePath)) { setupMainDb(); } } void LogCache::setupMainDb() { #ifndef NO_SQLITE3 if (!TQSqlDatabase::isDriverAvailable(SQLTYPE)) { TQSqlDatabase::registerSqlDriver(SQLTYPE,new TQSqlDriverCreator); } #endif TQDataBase mainDB = m_CacheData->getMainDB(); if (!mainDB || !mainDB->open()) { qWarning("%s", TQString("Failed to open main database: " + (mainDB?mainDB->lastError().text():"No database object.")).ascii()); } else { TQSqlQuery q(TQString(), mainDB); mainDB->transaction(); if (!q.exec("CREATE TABLE IF NOT EXISTS \""+TQString(SQLMAINTABLE)+"\" (\"reposroot\" TEXT,\"id\" INTEGER PRIMARY KEY NOT NULL);")) { qWarning("%s", TQString("Failed create main database: " + mainDB->lastError().text()).ascii()); } mainDB->commit(); } } } } /*! \fn svn::cache::LogCache::self() */ svn::cache::LogCache* svn::cache::LogCache::self() { if (!mSelf) { mSelf=new LogCache(); } return mSelf; } /*! \fn svn::cache::LogCache::reposDb() */ TQDataBase svn::cache::LogCache::reposDb(const TQString&aRepository) { // qDebug("reposDB"); return m_CacheData->getReposDB(aRepository); } /*! \fn svn::cache::LogCache::cachedRepositories()const */ TQStringList svn::cache::LogCache::cachedRepositories()const { static TQString s_q(TQString("select \"reposroot\" from ")+TQString(SQLMAINTABLE)+TQString("order by reposroot")); TQDataBase mainDB = m_CacheData->getMainDB(); TQStringList _res; if (!mainDB || !mainDB->open()) { qWarning("Failed to open main database."); return _res; } TQSqlQuery cur(TQString(),mainDB); cur.prepare(s_q); if (!cur.exec()) { qDebug("%s", TQString(cur.lastError().text().TOUTF8().data()).ascii()); throw svn::cache::DatabaseException(TQString("Could not retrieve values: ")+cur.lastError().text()); return _res; } while (cur.next()) { _res.append(cur.value(0).toString()); } return _res; } bool svn::cache::LogCache::valid()const { TQDataBase mainDB = m_CacheData->getMainDB(); if (!mainDB || !mainDB->open()) { return false; } return true; }