diff --git a/src/svnqt/cache/LogCache.cpp b/src/svnqt/cache/LogCache.cpp index cb357008..35d22cea 100644 --- a/src/svnqt/cache/LogCache.cpp +++ b/src/svnqt/cache/LogCache.cpp @@ -1,583 +1,583 @@ /*************************************************************************** * Copyright (C) 2005-2009 by Rajko Albrecht ral@alwins-world.de * * http://kdesvn.alwins-world.de/ * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 of the License, 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 * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this program (in the file LGPL.txt); if not, * * write to the Free Software Foundation, Inc., 51 Franklin St, * * Fifth Floor, Boston, MA 02110-1301 USA * * * * This software consists of voluntary contributions made by many * * individuals. For exact contribution history, see the revision * * history and logs, available at http://kdesvn.alwins-world.de. * ***************************************************************************/ #include "LogCache.h" #include #include #include #include #include #include #include #include #include #include "svnqt/path.h" #include "svnqt/cache/DatabaseException.h" static QString SQLTYPE() { return QStringLiteral("QSQLITE"); } static QString SQLMAIN() { return QStringLiteral("logmain-logcache"); } static QString SQLMAINTABLE() { return QStringLiteral("logdb"); } static QString SQLTMPDB() { return QStringLiteral("tmpdb"); } static QString SQLREPOSPARAMETER() { return QStringLiteral("repoparameter"); } static QString SQLSTATUS() { return QStringLiteral("logstatus"); } namespace svn { namespace cache { LogCache *LogCache::mSelf = nullptr; class ThreadDBStore { public: ThreadDBStore() { m_DB = QSqlDatabase(); } ~ThreadDBStore() { m_DB.commit(); m_DB.close(); m_DB = QSqlDatabase(); - QMap::Iterator it; - for (it = reposCacheNames.begin(); it != reposCacheNames.end(); ++it) { - if (QSqlDatabase::database(it.value()).isOpen()) { - QSqlDatabase::database(it.value()).commit(); - QSqlDatabase::database(it.value()).close(); + for (const QString &dbName : reposCacheNames) { + if (QSqlDatabase::database(dbName).isOpen()) { + QSqlDatabase::database(dbName).commit(); + QSqlDatabase::database(dbName).close(); } - QSqlDatabase::removeDatabase(it.value()); + QSqlDatabase::removeDatabase(dbName); } QSqlDatabase::removeDatabase(key); } void deleteDb(const QString &path) { - QMap::Iterator it; - for (it = reposCacheNames.begin(); it != reposCacheNames.end(); ++it) { + for (auto it = reposCacheNames.begin(); it != reposCacheNames.end(); ++it) { QSqlDatabase _db = QSqlDatabase::database(it.value()); if (_db.databaseName() == path) { qDebug() << "Removing database " << _db.databaseName() << endl; if (_db.isOpen()) { _db.commit(); _db.close(); } + _db = QSqlDatabase(); QSqlDatabase::removeDatabase(it.value()); - it = reposCacheNames.begin(); + reposCacheNames.erase(it); + break; } } } QSqlDatabase m_DB; QString key; QMap reposCacheNames; }; class LogCacheData { protected: QMutex m_singleDbMutex; public: LogCacheData() {} ~LogCacheData() { if (m_mainDB.hasLocalData()) { m_mainDB.localData()->m_DB.close(); - m_mainDB.setLocalData(0L); + m_mainDB.setLocalData(nullptr); } } QString idToPath(const QString &id) const { return m_BasePath + QLatin1Char('/') + id + QLatin1String(".db"); } bool deleteRepository(const QString &aRepository) { const QString id = getReposId(aRepository); static const QString s_q(QLatin1String("delete from ") + SQLREPOSPARAMETER() + QLatin1String(" where id = ?")); static const QString r_q(QLatin1String("delete from ") + SQLMAINTABLE() + QLatin1String(" where id = ?")); QSqlDatabase mainDB = getMainDB(); if (!mainDB.isValid()) { qWarning("Failed to open main database."); return false; } qDebug() << m_mainDB.localData()->reposCacheNames; m_mainDB.localData()->deleteDb(idToPath(id)); qDebug() << m_mainDB.localData()->reposCacheNames; QFile fi(idToPath(id)); if (fi.exists()) { if (!fi.remove()) { qWarning() << "Could not delete " << fi.fileName(); return false; } } qDebug() << "Removed " << fi.fileName() << endl; mainDB.transaction(); QSqlQuery _q(mainDB); _q.prepare(s_q); _q.bindValue(0, id); if (!_q.exec()) { qDebug() << "Error delete value: " << _q.lastError().text() << "(" << _q.lastQuery() << ")"; _q.finish(); mainDB.rollback(); return false; } _q.prepare(r_q); _q.bindValue(0, id); if (!_q.exec()) { qDebug() << "Error delete value: " << _q.lastError().text() << "(" << _q.lastQuery() << ")"; _q.finish(); mainDB.rollback(); return false; } mainDB.commit(); return true; } bool checkReposDb(QSqlDatabase aDb) { if (!aDb.open()) { return false; } QSqlQuery _q(aDb); QStringList list = aDb.tables(); aDb.transaction(); if (!list.contains(QStringLiteral("logentries"))) { _q.exec(QStringLiteral("CREATE TABLE \"logentries\" (\"idx\" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, \"revision\" INTEGER UNIQUE,\"date\" INTEGER,\"author\" TEXT, \"message\" TEXT)")); } if (!list.contains(QStringLiteral("changeditems"))) { _q.exec(QStringLiteral("CREATE TABLE \"changeditems\" (\"idx\" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, \"revision\" INTEGER,\"changeditem\" TEXT,\"action\" TEXT,\"copyfrom\" TEXT,\"copyfromrev\" INTEGER, UNIQUE(revision,changeditem,action))")); } if (!list.contains(QStringLiteral("mergeditems"))) { _q.exec(QStringLiteral("CREATE TABLE \"mergeditems\" (\"revision\" INTEGER,\"mergeditems\" TEXT, PRIMARY KEY(revision))")); } if (!list.contains(QStringLiteral("dbversion"))) { _q.exec(QStringLiteral("CREATE TABLE \"dbversion\" (\"version\" INTEGER)")); qDebug() << _q.lastError(); _q.exec(QStringLiteral("INSERT INTO \"dbversion\" (version) VALUES(0)")); } aDb.commit(); list = aDb.tables(); if (!list.contains(QStringLiteral("logentries")) || !list.contains(QStringLiteral("changeditems")) || !list.contains(QStringLiteral("mergeditems")) || !list.contains(QStringLiteral("dbversion"))) { qDebug() << "lists: " << list; return false; } _q.exec(QStringLiteral("SELECT VERSION from dbversion limit 1")); if (_q.lastError().type() == QSqlError::NoError && _q.next()) { int version = _q.value(0).toInt(); if (version == 0) { _q.exec(QStringLiteral("create index if not exists main.authorindex on logentries(author)")); if (_q.lastError().type() != QSqlError::NoError) { qDebug() << _q.lastError(); } else { _q.exec(QStringLiteral("UPDATE dbversion SET VERSION=1")); } ++version; } if (version == 1) { _q.exec(QStringLiteral("create index if not exists main.dateindex on logentries(date)")); if (_q.lastError().type() != QSqlError::NoError) { qDebug() << _q.lastError(); } else { _q.exec(QStringLiteral("UPDATE dbversion SET VERSION=2")); } ++version; } } else { qDebug() << "Select: " << _q.lastError(); } return true; } QString createReposDB(const svn::Path &reposroot) { QMutexLocker locker(&m_singleDbMutex); QSqlDatabase _mdb = getMainDB(); _mdb.transaction(); QSqlQuery query(_mdb); QString q(QLatin1String("insert into ") + SQLMAINTABLE() + QLatin1String(" (reposroot) VALUES('") + reposroot.path() + QLatin1String("')")); if (!query.exec(q)) { return QString(); } _mdb.commit(); query.prepare(reposSelect()); query.bindValue(0, reposroot.native()); QString db; if (query.exec() && query.next()) { db = query.value(0).toString(); } else { //qDebug() << "Error select_01: " << query.lastError().text() << "(" << query.lastQuery() << ")"; } if (!db.isEmpty()) { QString fulldb = idToPath(db); QSqlDatabase _db = QSqlDatabase::addDatabase(SQLTYPE(), SQLTMPDB()); _db.setDatabaseName(fulldb); if (!checkReposDb(_db)) { } + _db = QSqlDatabase(); QSqlDatabase::removeDatabase(SQLTMPDB()); } return db; } QString getReposId(const svn::Path &reposroot) { if (!getMainDB().isValid()) { return QString(); } QSqlQuery c(getMainDB()); c.prepare(reposSelect()); c.bindValue(0, reposroot.native()); // only the first one if (c.exec() && c.next()) { return c.value(0).toString(); } return QString(); } QSqlDatabase getReposDB(const svn::Path &reposroot) { if (!getMainDB().isValid()) { return QSqlDatabase(); } QString dbFile = getReposId(reposroot); if (dbFile.isEmpty()) { dbFile = createReposDB(reposroot); if (dbFile.isEmpty()) { return QSqlDatabase(); } } if (m_mainDB.localData()->reposCacheNames.find(dbFile) != m_mainDB.localData()->reposCacheNames.end()) { QSqlDatabase db = QSqlDatabase::database(m_mainDB.localData()->reposCacheNames.value(dbFile)); checkReposDb(db); return db; } unsigned i = 0; QString _key = dbFile; while (QSqlDatabase::contains(_key)) { _key = QStringLiteral("%1-%2").arg(dbFile).arg(i++); } const QString fulldb = idToPath(dbFile); QSqlDatabase db = QSqlDatabase::addDatabase(SQLTYPE(), _key); db.setDatabaseName(fulldb); if (!checkReposDb(db)) { db = QSqlDatabase(); } else { m_mainDB.localData()->reposCacheNames[dbFile] = _key; } return db; } QSqlDatabase getMainDB()const { if (!m_mainDB.hasLocalData()) { unsigned i = 0; QString _key = SQLMAIN(); while (QSqlDatabase::contains(_key)) { _key = QStringLiteral("%1-%2").arg(SQLMAIN()).arg(i++); } QSqlDatabase db = QSqlDatabase::addDatabase(SQLTYPE(), _key); db.setDatabaseName(m_BasePath + QLatin1String("/maindb.db")); if (db.open()) { 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 QSqlDatabase(); } } QString m_BasePath; mutable QThreadStorage m_mainDB; static QString reposSelect() { return QStringLiteral("SELECT id from ") + SQLMAINTABLE() + QStringLiteral(" where reposroot=? ORDER by id DESC"); } }; /*! \fn svn::cache::LogCache::LogCache() */ LogCache::LogCache() : m_BasePath(QDir::homePath() + QLatin1String("/.svnqt")) { setupCachePath(); } LogCache::LogCache(const QString &aBasePath) { delete mSelf; mSelf = this; if (aBasePath.isEmpty()) { m_BasePath = QDir::homePath() + QLatin1String("/.svnqt"); } else { m_BasePath = aBasePath; } setupCachePath(); } LogCache::~LogCache() { } /*! \fn svn::cache::LogCache::setupCachePath() */ void LogCache::setupCachePath() { m_CacheData.reset(new LogCacheData); m_CacheData->m_BasePath = m_BasePath; QDir d; if (!d.exists(m_BasePath)) { d.mkdir(m_BasePath); } m_BasePath = m_BasePath + QLatin1Char('/') + QLatin1String("logcache"); if (!d.exists(m_BasePath)) { d.mkdir(m_BasePath); } m_CacheData->m_BasePath = m_BasePath; if (d.exists(m_BasePath)) { setupMainDb(); } } void LogCache::setupMainDb() { QSqlDatabase mainDB = m_CacheData->getMainDB(); if (!mainDB.isValid()) { qWarning("Failed to open main database."); } else { const QStringList list = mainDB.tables(); QSqlQuery q(mainDB); if (!list.contains(SQLSTATUS())) { mainDB.transaction(); if (q.exec(QLatin1String("CREATE TABLE \"") + SQLSTATUS() + QLatin1String("\" (\"key\" TEXT PRIMARY KEY NOT NULL, \"value\" TEXT);"))) { q.exec(QLatin1String("INSERT INTO \"") + SQLSTATUS() + QLatin1String("\" (key,value) values(\"version\",\"0\");")); } mainDB.commit(); } int version = databaseVersion(); if (version == 0) { mainDB.transaction(); if (!list.contains(SQLMAINTABLE())) { q.exec(QLatin1String("CREATE TABLE IF NOT EXISTS \"") + SQLMAINTABLE() + QLatin1String("\" (\"reposroot\" TEXT,\"id\" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL);")); }/* else { q.exec("CREATE TABLE IF NOT EXISTS \""+QString(SQLMAINTABLE)+"new\" (\"reposroot\" TEXT,\"id\" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL);"); q.exec("insert into \""+QString(SQLMAINTABLE)+"new\" select \"reposroot\",\"id\" from \""+QString(SQLMAINTABLE)+"\");"); q.exec("drop table \""+QString(SQLMAINTABLE)+"\";"); q.exec("alter table \""+QString(SQLMAINTABLE)+"new\" to \""+QString(SQLMAINTABLE)+"\";"); }*/ ++version; } if (version == 1) { mainDB.transaction(); if (!q.exec(QLatin1String("CREATE TABLE IF NOT EXISTS \"") + SQLREPOSPARAMETER() + QLatin1String("\" (\"id\" INTEGER NOT NULL, \"parameter\" TEXT, \"value\" TEXT, PRIMARY KEY(\"id\",\"parameter\"));"))) { qDebug() << "Error create: " << q.lastError().text() << "(" << q.lastQuery() << ")"; } mainDB.commit(); ++version; } databaseVersion(version); } } void LogCache::databaseVersion(int newversion) { QSqlDatabase mainDB = m_CacheData->getMainDB(); if (!mainDB.isValid()) { return; } static const QString _qs(QLatin1String("update \"") + SQLSTATUS() + QLatin1String("\" SET value = ? WHERE \"key\" = \"version\"")); QSqlQuery cur(mainDB); cur.prepare(_qs); cur.bindValue(0, newversion); if (!cur.exec()) { qDebug() << "Error set version: " << cur.lastError().text() << "(" << cur.lastQuery() << ")"; } } int LogCache::databaseVersion()const { QSqlDatabase mainDB = m_CacheData->getMainDB(); if (!mainDB.isValid()) { return -1; } static const QString _qs(QLatin1String("select value from \"") + SQLSTATUS() + QLatin1String("\" WHERE \"key\" = \"version\"")); QSqlQuery cur(mainDB); cur.prepare(_qs); if (!cur.exec()) { qDebug() << "Error select version: " << cur.lastError().text() << "(" << cur.lastQuery() << ")"; return -1; } if (cur.isActive() && cur.next()) { //qDebug("Sel result: %s",_q.value(0).toString().toUtf8().data()); return cur.value(0).toInt(); } return -1; } QVariant LogCache::getRepositoryParameter(const svn::Path &repository, const QString &key)const { QSqlDatabase mainDB = m_CacheData->getMainDB(); if (!mainDB.isValid()) { return QVariant(); } static const QString qs(QLatin1String("select \"value\",\"repoparameter\".\"parameter\" as \"key\" from \"") + SQLREPOSPARAMETER() + QLatin1String("\" INNER JOIN \"") + SQLMAINTABLE() + QLatin1String("\" ON (\"") + SQLREPOSPARAMETER() + QLatin1String("\".id = \"") + SQLMAINTABLE() + QLatin1String("\".id and \"") + SQLMAINTABLE() + QLatin1String("\".reposroot = ?) WHERE \"parameter\" = ?;")); QSqlQuery cur(mainDB); cur.prepare(qs); cur.bindValue(0, repository.native()); cur.bindValue(1, key); if (!cur.exec()) { qWarning() << "Error select: " << cur.lastError().text() << "(" << cur.lastQuery() << ")"; return QVariant(); } if (cur.isActive() && cur.next()) { return cur.value(0); } return QVariant(); } bool LogCache::setRepositoryParameter(const svn::Path &repository, const QString &key, const QVariant &value) { QSqlDatabase mainDB = m_CacheData->getMainDB(); if (!mainDB.isValid()) { return false; } QString id = m_CacheData->getReposId(repository); if (id.isEmpty()) { return false; } static const QString qs(QLatin1String("INSERT OR REPLACE INTO \"") + SQLREPOSPARAMETER() + QLatin1String("\" (\"id\",\"parameter\",\"value\") values (\"%1\",\"%2\",?);")); static const QString dqs(QLatin1String("DELETE FROM \"") + SQLREPOSPARAMETER() + QLatin1String("\" WHERE \"id\"=? and \"parameter\" = ?")); mainDB.transaction(); QSqlQuery cur(mainDB); if (value.isValid()) { QString _qs = qs.arg(id,key);//.arg(value.toByteArray()); cur.prepare(_qs); cur.bindValue(0, value); if (!cur.exec()) { qDebug() << "Error insert new value: " << cur.lastError().text() << "(" << cur.lastQuery() << ")"; cur.finish(); mainDB.rollback(); return false; } } else { cur.prepare(dqs); cur.bindValue(0, id); cur.bindValue(1, key); if (!cur.exec()) { qDebug() << "Error delete value: " << cur.lastError().text() << "(" << cur.lastQuery() << ")"; cur.finish(); mainDB.rollback(); return false; } } mainDB.commit(); return true; } } } /*! \fn svn::cache::LogCache::self() */ svn::cache::LogCache *svn::cache::LogCache::self() { if (!mSelf) { mSelf = new LogCache(); } return mSelf; } /*! \fn svn::cache::LogCache::reposDb() */ QSqlDatabase svn::cache::LogCache::reposDb(const QString &aRepository) { // //qDebug("reposDB"); return m_CacheData->getReposDB(aRepository); } /*! \fn svn::cache::LogCache::cachedRepositories()const */ QStringList svn::cache::LogCache::cachedRepositories()const { static const QString s_q(QLatin1String("select \"reposroot\" from ") + SQLMAINTABLE() + QLatin1String(" order by reposroot")); QSqlDatabase mainDB = m_CacheData->getMainDB(); QStringList _res; if (!mainDB.isValid()) { qWarning("Failed to open main database."); return _res; } QSqlQuery cur(mainDB); cur.prepare(s_q); if (!cur.exec()) { throw svn::cache::DatabaseException(QLatin1String("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 { return m_CacheData->getMainDB().isValid(); } bool svn::cache::LogCache::deleteRepository(const QString &aRepository) { return m_CacheData->deleteRepository(aRepository); } diff --git a/src/svnqt/cache/ReposLog.cpp b/src/svnqt/cache/ReposLog.cpp index 0273d7ac..fa17aaa1 100644 --- a/src/svnqt/cache/ReposLog.cpp +++ b/src/svnqt/cache/ReposLog.cpp @@ -1,629 +1,623 @@ /*************************************************************************** * Copyright (C) 2005-2009 by Rajko Albrecht ral@alwins-world.de * * http://kdesvn.alwins-world.de/ * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 of the License, 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 * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this program (in the file LGPL.txt); if not, * * write to the Free Software Foundation, Inc., 51 Franklin St, * * Fifth Floor, Boston, MA 02110-1301 USA * * * * This software consists of voluntary contributions made by many * * individuals. For exact contribution history, see the revision * * history and logs, available at http://kdesvn.alwins-world.de. * ***************************************************************************/ #include "ReposLog.h" #include "LogCache.h" #include "svnqt/info_entry.h" #include "svnqt/svnqttypes.h" #include "svnqt/client.h" #include "svnqt/context_listener.h" #include "svnqt/cache/DatabaseException.h" #include "svnqt/client_parameter.h" #include #include #include #include #include #include #include #define Q_LLONG qlonglong class DatabaseLocker { public: DatabaseLocker(QSqlDatabase *db) : m_commited(false) , m_db(db) { m_db->transaction(); } ~DatabaseLocker() { if (!m_commited) { m_db->rollback(); } } void commit() { if (m_commited) { return; } m_db->commit(); m_commited = true; } protected: bool m_commited; QSqlDatabase *m_db; }; /*! \fn svn::cache::ReposLog::ReposLog(svn::Client*aClient,const QString&) */ svn::cache::ReposLog::ReposLog(const svn::ClientP &aClient, const QString &aRepository) : m_Client(aClient) , m_Database() , m_ReposRoot(aRepository) , m_latestHead(svn::Revision::UNDEFINED) { if (!aRepository.isEmpty()) { m_Database = LogCache::self()->reposDb(aRepository); } } /*! \fn svn::cache::ReposLog::latestHeadRev() */ svn::Revision svn::cache::ReposLog::latestHeadRev() { if (!m_Client || m_ReposRoot.isEmpty()) { return svn::Revision::UNDEFINED; } if (!m_Database.isValid()) { m_Database = LogCache::self()->reposDb(m_ReposRoot); if (!m_Database.isValid()) { return svn::Revision::UNDEFINED; } } /// no catch - exception has go trough... //qDebug("Getting headrev"); - svn::InfoEntries e = m_Client->info(m_ReposRoot, svn::DepthEmpty, svn::Revision::HEAD, svn::Revision::HEAD); + const svn::InfoEntries e = m_Client->info(m_ReposRoot, svn::DepthEmpty, svn::Revision::HEAD, svn::Revision::HEAD); if (e.count() < 1 || e[0].reposRoot().isEmpty()) { return svn::Revision::UNDEFINED; } //qDebug("Getting headrev done"); return e[0].revision(); } /*! \fn svn::cache::ReposLog::latestCachedRev() */ svn::Revision svn::cache::ReposLog::latestCachedRev() { if (m_ReposRoot.isEmpty()) { return svn::Revision::UNDEFINED; } if (!m_Database.isValid()) { m_Database = LogCache::self()->reposDb(m_ReposRoot); if (!m_Database.isValid()) { return svn::Revision::UNDEFINED; } } static const QLatin1String q("select revision from 'logentries' order by revision DESC limit 1"); QSqlQuery _q(QString(), m_Database); if (!_q.exec(q)) { //qDebug() << _q.lastError().text(); return svn::Revision::UNDEFINED; } int _r; if (_q.isActive() && _q.next()) { //qDebug("Sel result: %s",_q.value(0).toString().toUtf8().data()); _r = _q.value(0).toInt(); } else { //qDebug() << _q.lastError().text(); return svn::Revision::UNDEFINED; } return _r; } qlonglong svn::cache::ReposLog::count()const { if (!m_Database.isValid()) { m_Database = LogCache::self()->reposDb(m_ReposRoot); if (!m_Database.isValid()) { return svn::Revision::UNDEFINED; } } static const QLatin1String q("select count(*) from 'logentries'"); QSqlQuery _q(QString(), m_Database); if (!_q.exec(q)) { //qDebug() << _q.lastError().text(); return -1; } qlonglong _r; QVariant v; if (_q.isActive() && _q.next()) { //qDebug("Sel result: %s",_q.value(0).toString().toUtf8().data()); v = _q.value(0); if (v.canConvert(QVariant::LongLong)) { bool ok = false; _r = v.toLongLong(&ok); if (ok) { return _r; } } } return -1; } qlonglong svn::cache::ReposLog::fileSize()const { if (!m_Database.isValid()) { m_Database = LogCache::self()->reposDb(m_ReposRoot); if (!m_Database.isValid()) { return -1; } } QFileInfo fi(m_Database.databaseName()); if (fi.exists()) { return fi.size(); } return -1; } qlonglong svn::cache::ReposLog::itemCount()const { if (!m_Database.isValid()) { m_Database = LogCache::self()->reposDb(m_ReposRoot); if (!m_Database.isValid()) { return -1; } } static const QLatin1String q("select count(*) from 'changeditems'"); QSqlQuery _q(QString(), m_Database); if (!_q.exec(q)) { //qDebug() << _q.lastError().text(); return -1; } qlonglong _r; QVariant v; if (_q.isActive() && _q.next()) { //qDebug("Sel result: %s",_q.value(0).toString().toUtf8().data()); v = _q.value(0); if (v.canConvert(QVariant::LongLong)) { bool ok = false; _r = v.toLongLong(&ok); if (ok) { return _r; } } } return -1; } bool svn::cache::ReposLog::checkFill(svn::Revision &start, svn::Revision &end, bool checkHead) { if (!m_Database.isValid()) { m_Database = LogCache::self()->reposDb(m_ReposRoot); if (!m_Database.isValid()) { return false; } } ContextP cp = m_Client->getContext(); // long long icount=0; svn::Revision _latest = latestCachedRev(); // //qDebug("Latest cached rev: %i",_latest.revnum()); // //qDebug("End revision is: %s",end.toString().toUtf8().data()); if (checkHead && _latest.revnum() >= latestHeadRev().revnum()) { return true; } start = date2numberRev(start); end = date2numberRev(end); // both should now one of START, HEAD or NUMBER if (start == svn::Revision::HEAD || (end == svn::Revision::NUMBER && start == svn::Revision::NUMBER && start.revnum() > end.revnum())) { svn::Revision tmp = start; start = end; end = tmp; } svn::Revision _rstart = _latest.revnum() + 1; svn::Revision _rend = end; if (_rend == svn::Revision::UNDEFINED) { // //qDebug("Setting end to Head"); _rend = svn::Revision::HEAD; } // no catch - exception should go outside. if (_rstart == 0) { _rstart = 1; } // //qDebug("Getting log %s -> %s",_rstart.toString().toUtf8().data(),_rend.toString().toUtf8().data()); if (_rend == svn::Revision::HEAD) { _rend = latestHeadRev(); } if (_rend == svn::Revision::HEAD || _rend.revnum() > _latest.revnum()) { LogEntriesMap _internal; // //qDebug("Retrieving from network."); LogParameter params; if (!m_Client->log(params.targets(m_ReposRoot).revisionRange(_rstart, _rend).peg(svn::Revision::UNDEFINED).discoverChangedPathes(true).strictNodeHistory(false), _internal)) { return false; } - LogEntriesMap::ConstIterator it = _internal.constBegin(); DatabaseLocker l(&m_Database); - for (; it != _internal.constEnd(); ++it) { - _insertLogEntry((*it)); + for (const LogEntry &le : qAsConst(_internal)) { + _insertLogEntry(le); if (cp && cp->getListener()) { //cp->getListener()->contextProgress(++icount,_internal.size()); if (cp->getListener()->contextCancel()) { throw DatabaseException(QStringLiteral("Could not retrieve values: User cancel.")); } } } l.commit(); } return true; } bool svn::cache::ReposLog::fillCache(const svn::Revision &_end) { svn::Revision end = _end; svn::Revision start = latestCachedRev().revnum() + 1; return checkFill(start, end, false); } /*! \fn svn::cache::ReposLog::simpleLog(const svn::Revision&start,const svn::Revision&end,LogEntriesMap&target) */ bool svn::cache::ReposLog::simpleLog(LogEntriesMap &target, const svn::Revision &_start, const svn::Revision &_end, bool noNetwork, const StringArray &exclude) { if (!m_Client || m_ReposRoot.isEmpty()) { return false; } target.clear(); ContextP cp = m_Client->getContext(); svn::Revision end = _end; svn::Revision start = _start; if (!noNetwork) { if (!checkFill(start, end, true)) { return false; } } else { end = date2numberRev(end, noNetwork); start = date2numberRev(start, noNetwork); } if (end == svn::Revision::HEAD) { end = latestCachedRev(); } if (start == svn::Revision::HEAD) { start = latestCachedRev(); } QSqlQuery bcount(m_Database); bcount.prepare(QStringLiteral("select count(*) from logentries where revision<=? and revision>=?")); bcount.bindValue(0, Q_LLONG(end.revnum())); bcount.bindValue(1, Q_LLONG(start.revnum())); if (!bcount.exec()) { //qDebug() << bcount.lastError().text(); throw svn::cache::DatabaseException(QLatin1String("Could not retrieve count: ") + bcount.lastError().text()); - return false; } if (!bcount.next() || bcount.value(0).toLongLong() < 1) { // we didn't found logs with this parameters return false; } QSqlQuery bcur(m_Database); bcur.setForwardOnly(true); bcur.prepare(QStringLiteral("select revision,author,date,message from logentries where revision<=? and revision>=?")); bcur.bindValue(0, Q_LLONG(end.revnum())); bcur.bindValue(1, Q_LLONG(start.revnum())); if (!bcur.exec()) { throw svn::cache::DatabaseException(QLatin1String("Could not retrieve values: ") + bcur.lastError().text()); - return false; } QString sItems(QStringLiteral("select changeditem,action,copyfrom,copyfromrev from changeditems where revision=?")); - for (int i = 0; i < exclude.size(); ++i) { - sItems += QLatin1String(" and changeditem not like '") + exclude[i] + QLatin1String("%'"); + for (const QString &ex : exclude.data()) { + sItems += QLatin1String(" and changeditem not like '") + ex + QLatin1String("%'"); } QSqlQuery cur(m_Database); cur.setForwardOnly(true); cur.prepare(sItems); while (bcur.next()) { const Q_LLONG revision = bcur.value(0).toLongLong(); cur.bindValue(0, revision); if (!cur.exec()) { //qDebug() << cur.lastError().text(); throw svn::cache::DatabaseException(QStringLiteral("Could not retrieve revision values: %1, %2") .arg(cur.lastError().text(), cur.lastError().nativeErrorCode())); - return false; } target[revision].revision = revision; target[revision].author = bcur.value(1).toString(); target[revision].date = bcur.value(2).toLongLong(); target[revision].message = bcur.value(3).toString(); while (cur.next()) { LogChangePathEntry lcp; QString ac = cur.value(1).toString(); lcp.action = ac[0].toLatin1(); lcp.copyFromPath = cur.value(2).toString(); lcp.path = cur.value(0).toString(); lcp.copyFromRevision = cur.value(3).toLongLong(); target[revision].changedPaths.push_back(lcp); } if (cp && cp->getListener()) { if (cp->getListener()->contextCancel()) { throw svn::cache::DatabaseException(QStringLiteral("Could not retrieve values: User cancel.")); - return false; } } } return true; } /*! \fn svn::cache::ReposLog::date2numberRev(const svn::Revision&) */ svn::Revision svn::cache::ReposLog::date2numberRev(const svn::Revision &aRev, bool noNetwork) { if (aRev != svn::Revision::DATE) { return aRev; } if (!m_Database.isValid()) { return svn::Revision::UNDEFINED; } QSqlQuery query(QStringLiteral("select revision,date from logentries order by revision desc limit 1"), m_Database); if (query.lastError().type() != QSqlError::NoError) { //qDebug() << query.lastError().text(); } bool must_remote = !noNetwork; if (query.next()) { if (query.value(1).toLongLong() >= aRev.date()) { must_remote = false; } } if (must_remote) { - svn::InfoEntries e = (m_Client->info(m_ReposRoot, svn::DepthEmpty, aRev, aRev));; + const svn::InfoEntries e = (m_Client->info(m_ReposRoot, svn::DepthEmpty, aRev, aRev));; if (e.count() < 1 || e[0].reposRoot().isEmpty()) { return aRev; } return e[0].revision(); } query.prepare(QStringLiteral("select revision from logentries where dateinfo(m_ReposRoot, svn::DepthEmpty, svn::Revision::HEAD, svn::Revision::HEAD));; + const svn::InfoEntries e = (m_Client->info(m_ReposRoot, svn::DepthEmpty, svn::Revision::HEAD, svn::Revision::HEAD));; if (e.count() < 1 || e[0].reposRoot().isEmpty()) { return svn::Revision::UNDEFINED; } return e[0].revision(); } /*! \fn svn::cache::ReposLog::insertLogEntry(const svn::LogEntry&) */ bool svn::cache::ReposLog::_insertLogEntry(const svn::LogEntry &aEntry) { qlonglong j = aEntry.revision; static const QLatin1String qEntry("insert into logentries (revision,date,author,message) values (?,?,?,?)"); static const QLatin1String qPathes("insert into changeditems (revision,changeditem,action,copyfrom,copyfromrev) values (?,?,?,?,?)"); QSqlQuery _q(QString(), m_Database); _q.prepare(qEntry); _q.bindValue(0, j); _q.bindValue(1, aEntry.date); _q.bindValue(2, aEntry.author); _q.bindValue(3, aEntry.message); if (!_q.exec()) { //qDebug("Could not insert values: %s",_q.lastError().text().toUtf8().data()); //qDebug() << _q.lastQuery(); throw svn::cache::DatabaseException(QStringLiteral("_insertLogEntry_0: Could not insert values: %1, %2").arg(_q.lastError().text(), _q.lastError().nativeErrorCode())); } _q.prepare(qPathes); - svn::LogChangePathEntries::ConstIterator cpit = aEntry.changedPaths.begin(); - for (; cpit != aEntry.changedPaths.end(); ++cpit) { + for (const LogChangePathEntry &cp : aEntry.changedPaths) { _q.bindValue(0, j); - _q.bindValue(1, (*cpit).path); - _q.bindValue(2, QString(QLatin1Char((*cpit).action))); - _q.bindValue(3, (*cpit).copyFromPath); - _q.bindValue(4, Q_LLONG((*cpit).copyFromRevision)); + _q.bindValue(1, cp.path); + _q.bindValue(2, QString(QLatin1Char(cp.action))); + _q.bindValue(3, cp.copyFromPath); + _q.bindValue(4, Q_LLONG(cp.copyFromRevision)); if (!_q.exec()) { //qDebug("Could not insert values: %s",_q.lastError().text().toUtf8().data()); //qDebug() << _q.lastQuery(); throw svn::cache::DatabaseException(QStringLiteral("Could not insert values: %1, %2").arg(_q.lastError().text(), _q.lastError().nativeErrorCode())); } } if (!aEntry.m_MergedInRevisions.isEmpty()) { static const QLatin1String qMerges("insert into mergeditems(revision,mergeditems) values(?,?)"); _q.prepare(qMerges); QByteArray _merges; QBuffer buffer(&_merges); buffer.open(QIODevice::ReadWrite); QDataStream af(&buffer); af << aEntry.m_MergedInRevisions; buffer.close(); _q.bindValue(0, j); _q.bindValue(1, _merges); if (!_q.exec()) { //qDebug("Could not insert values: %s",_q.lastError().text().toUtf8().data()); //qDebug() << _q.lastQuery(); throw svn::cache::DatabaseException(QStringLiteral("Could not insert values: %1, %2").arg(_q.lastError().text(), _q.lastError().nativeErrorCode())); } } return true; } bool svn::cache::ReposLog::insertLogEntry(const svn::LogEntry &aEntry) { DatabaseLocker l(&m_Database); if (!_insertLogEntry(aEntry)) { return false; } l.commit(); return true; } /*! \fn svn::cache::ReposLog::log(const svn::Path&,const svn::Revision&start, const svn::Revision&end,const svn::Revision&peg,svn::LogEntriesMap&target, bool strictNodeHistory,int limit)) */ bool svn::cache::ReposLog::log(const svn::Path &what, const svn::Revision &_start, const svn::Revision &_end, const svn::Revision &_peg, svn::LogEntriesMap &target, bool strictNodeHistory, int limit) { Q_UNUSED(strictNodeHistory); static const QLatin1String s_q("select logentries.revision,logentries.author,logentries.date,logentries.message from logentries where logentries.revision in (select changeditems.revision from changeditems where (changeditems.changeditem='%1' or changeditems.changeditem GLOB '%2/*') %3 GROUP BY changeditems.revision) ORDER BY logentries.revision DESC"); static const QLatin1String s_e("select changeditem,action,copyfrom,copyfromrev from changeditems where changeditems.revision='%1'"); static const QLatin1String s_m("select mergeditems from mergeditems where mergeditems.revision='%1'"); svn::Revision peg = date2numberRev(_peg, true); QString query_string = QString(s_q).arg(what.native(), what.native(), (peg == svn::Revision::UNDEFINED ? QString() : QStringLiteral(" AND revision<=%1").arg(peg.revnum()))); if (peg == svn::Revision::UNDEFINED) { peg = latestCachedRev(); } if (!itemExists(peg, what)) { throw svn::cache::DatabaseException(QStringLiteral("Entry '%1' does not exists at revision %2").arg(what.native(), peg.toString())); } if (limit > 0) { query_string += QStringLiteral(" LIMIT %1").arg(limit); } QSqlQuery _q(m_Database); QSqlQuery _q2(m_Database); _q.setForwardOnly(true); _q.prepare(query_string); if (!_q.exec()) { //qDebug("Could not select values: %s",_q.lastError().text().toUtf8().data()); //qDebug() << _q.lastQuery(); throw svn::cache::DatabaseException(QStringLiteral("Could not select values: %1, %2").arg(_q.lastError().text(), _q.lastError().nativeErrorCode())); } while (_q.next()) { Q_LLONG revision = _q.value(0).toLongLong(); target[revision].revision = revision; target[revision].author = _q.value(1).toString(); target[revision].date = _q.value(2).toLongLong(); target[revision].message = _q.value(3).toString(); query_string = QString(s_e).arg(revision); _q2.setForwardOnly(true); _q2.prepare(query_string); if (!_q2.exec()) { //qDebug("Could not select values: %s",_q2.lastError().text().toUtf8().data()); } else { while (_q2.next()) { target[revision].changedPaths.push_back( LogChangePathEntry(_q2.value(0).toString(), _q2.value(1).toChar().toLatin1(), _q2.value(2).toString(), _q2.value(3).toLongLong() ) ); } } query_string = QString(s_m).arg(revision); _q2.prepare(query_string); if (!_q2.exec()) { //qDebug("Could not select values: %s",_q2.lastError().text().toUtf8().data()); } else { if (_q2.next()) { QByteArray byteArray = _q2.value(0).toByteArray(); QBuffer buffer(&byteArray); QDataStream in(&buffer); in >> target[revision].m_MergedInRevisions; } } } return true; } /*! \fn svn::cache::ReposLog::itemExists(const svn::Revision&,const QString&) */ bool svn::cache::ReposLog::itemExists(const svn::Revision &peg, const svn::Path &path) { /// @todo this moment I have no idea how to check real with all moves and deletes of parent folders without a hell of sql statements so we make it quite simple: it exists if we found it. Q_UNUSED(peg); Q_UNUSED(path); #if 0 static QString _s1("select revision from changeditems where changeditem='%1' and action='A' and revision<=%2 order by revision desc limit 1"); QSqlQuery _q(QString(), m_Database); QString query_string = QString(_s1).arg(path.native()).arg(peg.revnum()); if (!_q.exec(query_string)) { //qDebug("Could not select values: %s",_q.lastError().text().toUtf8().data()); //qDebug(_q.lastQuery().toUtf8().data()); throw svn::cache::DatabaseException(QString("Could not select values: ") + _q.lastError().text(), _q.lastError().number()); } //qDebug(_q.lastQuery().toUtf8().data()); svn::Path _p = path; static QString _s2("select revision from changeditem where changeditem in (%1) and action='D' and revision>%2 and revision<=%3 order by revision desc limit 1"); QStringList p_list; while (_p.length() > 0) { p_list.append(QString("'%1'").arg(_p.native())); _p.removeLast(); } query_string = QString(_s2).arg(p_list.join(",")).arg(); #endif return true; } bool svn::cache::ReposLog::isValid()const { if (!m_Database.isValid()) { m_Database = LogCache::self()->reposDb(m_ReposRoot); if (!m_Database.isValid()) { return false; } } return true; } void svn::cache::ReposLog::cleanLogEntries() { if (!isValid()) { return; } DatabaseLocker l(&m_Database); QSqlQuery _q(QString(), m_Database); if (!_q.exec(QStringLiteral("delete from logentries"))) { return; } if (!_q.exec(QStringLiteral("delete from changeditems"))) { return; } if (!_q.exec(QStringLiteral("delete from mergeditems"))) { return; } l.commit(); _q.exec(QStringLiteral("vacuum")); }