File indexing completed on 2024-05-12 05:44:35

0001 /***************************************************************************
0002  *   Copyright (C) 2005-2009 by Rajko Albrecht  ral@alwins-world.de        *
0003  *   https://kde.org/applications/development/org.kde.kdesvn               *
0004  *                                                                         *
0005  * This program is free software; you can redistribute it and/or           *
0006  * modify it under the terms of the GNU Lesser General Public              *
0007  * License as published by the Free Software Foundation; either            *
0008  * version 2.1 of the License, or (at your option) any later version.      *
0009  *                                                                         *
0010  * This program is distributed in the hope that it will be useful,         *
0011  * but WITHOUT ANY WARRANTY; without even the implied warranty of          *
0012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU       *
0013  * Lesser General Public License for more details.                         *
0014  *                                                                         *
0015  * You should have received a copy of the GNU Lesser General Public        *
0016  * License along with this program (in the file LGPL.txt); if not,         *
0017  * write to the Free Software Foundation, Inc., 51 Franklin St,            *
0018  * Fifth Floor, Boston, MA  02110-1301  USA                                *
0019  *                                                                         *
0020  * This software consists of voluntary contributions made by many          *
0021  * individuals.  For exact contribution history, see the revision          *
0022  * history and logs, available at https://commits.kde.org/kdesvn.          *
0023  ***************************************************************************/
0024 #include "ReposLog.h"
0025 
0026 #include "LogCache.h"
0027 #include "svnqt/cache/DatabaseException.h"
0028 #include "svnqt/client.h"
0029 #include "svnqt/client_parameter.h"
0030 #include "svnqt/context_listener.h"
0031 #include "svnqt/info_entry.h"
0032 #include "svnqt/svnqttypes.h"
0033 
0034 #include <QBuffer>
0035 #include <QDataStream>
0036 #include <QFileInfo>
0037 #include <QSqlDatabase>
0038 #include <QSqlError>
0039 #include <QSqlQuery>
0040 #include <QVariant>
0041 #define Q_LLONG qlonglong
0042 
0043 class DatabaseLocker
0044 {
0045 public:
0046     DatabaseLocker(QSqlDatabase *db)
0047         : m_commited(false)
0048         , m_db(db)
0049     {
0050         m_db->transaction();
0051     }
0052     ~DatabaseLocker()
0053     {
0054         if (!m_commited) {
0055             m_db->rollback();
0056         }
0057     }
0058 
0059     void commit()
0060     {
0061         if (m_commited) {
0062             return;
0063         }
0064         m_db->commit();
0065         m_commited = true;
0066     }
0067 
0068 protected:
0069     bool m_commited;
0070     QSqlDatabase *m_db;
0071 };
0072 
0073 /*!
0074     \fn svn::cache::ReposLog::ReposLog(svn::Client*aClient,const QString&)
0075  */
0076 svn::cache::ReposLog::ReposLog(const svn::ClientP &aClient, const QString &aRepository)
0077     : m_Client(aClient)
0078     , m_Database()
0079     , m_ReposRoot(aRepository)
0080     , m_latestHead(svn::Revision::UNDEFINED)
0081 {
0082     if (!aRepository.isEmpty()) {
0083         m_Database = LogCache::self()->reposDb(aRepository);
0084     }
0085 }
0086 
0087 /*!
0088     \fn svn::cache::ReposLog::latestHeadRev()
0089  */
0090 svn::Revision svn::cache::ReposLog::latestHeadRev()
0091 {
0092     if (!m_Client || m_ReposRoot.isEmpty()) {
0093         return svn::Revision::UNDEFINED;
0094     }
0095     if (!m_Database.isValid()) {
0096         m_Database = LogCache::self()->reposDb(m_ReposRoot);
0097         if (!m_Database.isValid()) {
0098             return svn::Revision::UNDEFINED;
0099         }
0100     }
0101     /// no catch - exception has go trough...
0102     // qDebug("Getting headrev");
0103     const svn::InfoEntries e = m_Client->info(m_ReposRoot, svn::DepthEmpty, svn::Revision::HEAD, svn::Revision::HEAD);
0104     if (e.count() < 1 || e[0].reposRoot().isEmpty()) {
0105         return svn::Revision::UNDEFINED;
0106     }
0107     // qDebug("Getting headrev done");
0108     return e[0].revision();
0109 }
0110 
0111 /*!
0112     \fn svn::cache::ReposLog::latestCachedRev()
0113  */
0114 svn::Revision svn::cache::ReposLog::latestCachedRev()
0115 {
0116     if (m_ReposRoot.isEmpty()) {
0117         return svn::Revision::UNDEFINED;
0118     }
0119     if (!m_Database.isValid()) {
0120         m_Database = LogCache::self()->reposDb(m_ReposRoot);
0121         if (!m_Database.isValid()) {
0122             return svn::Revision::UNDEFINED;
0123         }
0124     }
0125     static const QLatin1String q("select revision from 'logentries' order by revision DESC limit 1");
0126     QSqlQuery _q(QString(), m_Database);
0127     if (!_q.exec(q)) {
0128         // qDebug() << _q.lastError().text();
0129         return svn::Revision::UNDEFINED;
0130     }
0131     int _r;
0132     if (_q.isActive() && _q.next()) {
0133         // qDebug("Sel result: %s",_q.value(0).toString().toUtf8().data());
0134         _r = _q.value(0).toInt();
0135     } else {
0136         // qDebug() << _q.lastError().text();
0137         return svn::Revision::UNDEFINED;
0138     }
0139     return _r;
0140 }
0141 
0142 qlonglong svn::cache::ReposLog::count() const
0143 {
0144     if (!m_Database.isValid()) {
0145         m_Database = LogCache::self()->reposDb(m_ReposRoot);
0146         if (!m_Database.isValid()) {
0147             return svn::Revision::UNDEFINED;
0148         }
0149     }
0150     static const QLatin1String q("select count(*) from 'logentries'");
0151     QSqlQuery _q(QString(), m_Database);
0152     if (!_q.exec(q)) {
0153         // qDebug() << _q.lastError().text();
0154         return -1;
0155     }
0156     qlonglong _r;
0157     QVariant v;
0158     if (_q.isActive() && _q.next()) {
0159         // qDebug("Sel result: %s",_q.value(0).toString().toUtf8().data());
0160         v = _q.value(0);
0161         if (v.canConvert(QVariant::LongLong)) {
0162             bool ok = false;
0163             _r = v.toLongLong(&ok);
0164             if (ok) {
0165                 return _r;
0166             }
0167         }
0168     }
0169     return -1;
0170 }
0171 
0172 qlonglong svn::cache::ReposLog::fileSize() const
0173 {
0174     if (!m_Database.isValid()) {
0175         m_Database = LogCache::self()->reposDb(m_ReposRoot);
0176         if (!m_Database.isValid()) {
0177             return -1;
0178         }
0179     }
0180     QFileInfo fi(m_Database.databaseName());
0181     if (fi.exists()) {
0182         return fi.size();
0183     }
0184     return -1;
0185 }
0186 
0187 qlonglong svn::cache::ReposLog::itemCount() const
0188 {
0189     if (!m_Database.isValid()) {
0190         m_Database = LogCache::self()->reposDb(m_ReposRoot);
0191         if (!m_Database.isValid()) {
0192             return -1;
0193         }
0194     }
0195     static const QLatin1String q("select count(*) from 'changeditems'");
0196     QSqlQuery _q(QString(), m_Database);
0197     if (!_q.exec(q)) {
0198         // qDebug() << _q.lastError().text();
0199         return -1;
0200     }
0201     qlonglong _r;
0202     QVariant v;
0203     if (_q.isActive() && _q.next()) {
0204         // qDebug("Sel result: %s",_q.value(0).toString().toUtf8().data());
0205         v = _q.value(0);
0206         if (v.canConvert(QVariant::LongLong)) {
0207             bool ok = false;
0208             _r = v.toLongLong(&ok);
0209             if (ok) {
0210                 return _r;
0211             }
0212         }
0213     }
0214     return -1;
0215 }
0216 
0217 bool svn::cache::ReposLog::checkFill(svn::Revision &start, svn::Revision &end, bool checkHead)
0218 {
0219     if (!m_Database.isValid()) {
0220         m_Database = LogCache::self()->reposDb(m_ReposRoot);
0221         if (!m_Database.isValid()) {
0222             return false;
0223         }
0224     }
0225     ContextP cp = m_Client->getContext();
0226     //     long long icount=0;
0227 
0228     svn::Revision _latest = latestCachedRev();
0229     //    //qDebug("Latest cached rev: %i",_latest.revnum());
0230     //    //qDebug("End revision is: %s",end.toString().toUtf8().data());
0231 
0232     if (checkHead && _latest.revnum() >= latestHeadRev().revnum()) {
0233         return true;
0234     }
0235 
0236     start = date2numberRev(start);
0237     end = date2numberRev(end);
0238 
0239     // both should now one of START, HEAD or NUMBER
0240     if (start == svn::Revision::HEAD || (end == svn::Revision::NUMBER && start == svn::Revision::NUMBER && start.revnum() > end.revnum())) {
0241         svn::Revision tmp = start;
0242         start = end;
0243         end = tmp;
0244     }
0245     svn::Revision _rstart = _latest.revnum() + 1;
0246     svn::Revision _rend = end;
0247     if (_rend == svn::Revision::UNDEFINED) {
0248         //        //qDebug("Setting end to Head");
0249         _rend = svn::Revision::HEAD;
0250     }
0251     // no catch - exception should go outside.
0252     if (_rstart == 0) {
0253         _rstart = 1;
0254     }
0255     //    //qDebug("Getting log %s -> %s",_rstart.toString().toUtf8().data(),_rend.toString().toUtf8().data());
0256     if (_rend == svn::Revision::HEAD) {
0257         _rend = latestHeadRev();
0258     }
0259 
0260     if (_rend == svn::Revision::HEAD || _rend.revnum() > _latest.revnum()) {
0261         LogEntriesMap _internal;
0262         //        //qDebug("Retrieving from network.");
0263         LogParameter params;
0264 
0265         if (!m_Client->log(
0266                 params.targets(m_ReposRoot).revisionRange(_rstart, _rend).peg(svn::Revision::UNDEFINED).discoverChangedPathes(true).strictNodeHistory(false),
0267                 _internal)) {
0268             return false;
0269         }
0270 
0271         DatabaseLocker l(&m_Database);
0272         for (const LogEntry &le : qAsConst(_internal)) {
0273             _insertLogEntry(le);
0274             if (cp && cp->getListener()) {
0275                 // cp->getListener()->contextProgress(++icount,_internal.size());
0276                 if (cp->getListener()->contextCancel()) {
0277                     throw DatabaseException(QStringLiteral("Could not retrieve values: User cancel."));
0278                 }
0279             }
0280         }
0281         l.commit();
0282     }
0283     return true;
0284 }
0285 
0286 bool svn::cache::ReposLog::fillCache(const svn::Revision &_end)
0287 {
0288     svn::Revision end = _end;
0289     svn::Revision start = latestCachedRev().revnum() + 1;
0290     return checkFill(start, end, false);
0291 }
0292 
0293 /*!
0294     \fn svn::cache::ReposLog::simpleLog(const svn::Revision&start,const svn::Revision&end,LogEntriesMap&target)
0295  */
0296 bool svn::cache::ReposLog::simpleLog(LogEntriesMap &target, const svn::Revision &_start, const svn::Revision &_end, bool noNetwork, const StringArray &exclude)
0297 {
0298     if (!m_Client || m_ReposRoot.isEmpty()) {
0299         return false;
0300     }
0301     target.clear();
0302     ContextP cp = m_Client->getContext();
0303 
0304     svn::Revision end = _end;
0305     svn::Revision start = _start;
0306     if (!noNetwork) {
0307         if (!checkFill(start, end, true)) {
0308             return false;
0309         }
0310     } else {
0311         end = date2numberRev(end, noNetwork);
0312         start = date2numberRev(start, noNetwork);
0313     }
0314 
0315     if (end == svn::Revision::HEAD) {
0316         end = latestCachedRev();
0317     }
0318     if (start == svn::Revision::HEAD) {
0319         start = latestCachedRev();
0320     }
0321 
0322     QSqlQuery bcount(m_Database);
0323     bcount.prepare(QStringLiteral("select count(*) from logentries where revision<=? and revision>=?"));
0324     bcount.bindValue(0, Q_LLONG(end.revnum()));
0325     bcount.bindValue(1, Q_LLONG(start.revnum()));
0326     if (!bcount.exec()) {
0327         // qDebug() << bcount.lastError().text();
0328         throw svn::cache::DatabaseException(QLatin1String("Could not retrieve count: ") + bcount.lastError().text());
0329     }
0330     if (!bcount.next() || bcount.value(0).toLongLong() < 1) {
0331         // we didn't found logs with this parameters
0332         return false;
0333     }
0334 
0335     QSqlQuery bcur(m_Database);
0336     bcur.setForwardOnly(true);
0337     bcur.prepare(QStringLiteral("select revision,author,date,message from logentries where revision<=? and revision>=?"));
0338     bcur.bindValue(0, Q_LLONG(end.revnum()));
0339     bcur.bindValue(1, Q_LLONG(start.revnum()));
0340 
0341     if (!bcur.exec()) {
0342         throw svn::cache::DatabaseException(QLatin1String("Could not retrieve values: ") + bcur.lastError().text());
0343     }
0344 
0345     QString sItems(QStringLiteral("select changeditem,action,copyfrom,copyfromrev from changeditems where revision=?"));
0346     for (const QString &ex : exclude.data()) {
0347         sItems += QLatin1String(" and changeditem not like '") + ex + QLatin1String("%'");
0348     }
0349     QSqlQuery cur(m_Database);
0350     cur.setForwardOnly(true);
0351     cur.prepare(sItems);
0352 
0353     while (bcur.next()) {
0354         const Q_LLONG revision = bcur.value(0).toLongLong();
0355         cur.bindValue(0, revision);
0356 
0357         if (!cur.exec()) {
0358             // qDebug() << cur.lastError().text();
0359             throw svn::cache::DatabaseException(
0360                 QStringLiteral("Could not retrieve revision values: %1, %2").arg(cur.lastError().text(), cur.lastError().nativeErrorCode()));
0361         }
0362         target[revision].revision = revision;
0363         target[revision].author = bcur.value(1).toString();
0364         target[revision].date = bcur.value(2).toLongLong();
0365         target[revision].message = bcur.value(3).toString();
0366         while (cur.next()) {
0367             LogChangePathEntry lcp;
0368             QString ac = cur.value(1).toString();
0369             lcp.action = ac[0].toLatin1();
0370             lcp.copyFromPath = cur.value(2).toString();
0371             lcp.path = cur.value(0).toString();
0372             lcp.copyFromRevision = cur.value(3).toLongLong();
0373             target[revision].changedPaths.push_back(lcp);
0374         }
0375         if (cp && cp->getListener()) {
0376             if (cp->getListener()->contextCancel()) {
0377                 throw svn::cache::DatabaseException(QStringLiteral("Could not retrieve values: User cancel."));
0378             }
0379         }
0380     }
0381     return true;
0382 }
0383 
0384 /*!
0385     \fn svn::cache::ReposLog::date2numberRev(const svn::Revision&)
0386  */
0387 svn::Revision svn::cache::ReposLog::date2numberRev(const svn::Revision &aRev, bool noNetwork)
0388 {
0389     if (aRev != svn::Revision::DATE) {
0390         return aRev;
0391     }
0392     if (!m_Database.isValid()) {
0393         return svn::Revision::UNDEFINED;
0394     }
0395     QSqlQuery query(QStringLiteral("select revision,date from logentries order by revision desc limit 1"), m_Database);
0396 
0397     if (query.lastError().type() != QSqlError::NoError) {
0398         // qDebug() << query.lastError().text();
0399     }
0400     bool must_remote = !noNetwork;
0401     if (query.next()) {
0402         if (query.value(1).toLongLong() >= aRev.date()) {
0403             must_remote = false;
0404         }
0405     }
0406     if (must_remote) {
0407         const svn::InfoEntries e = (m_Client->info(m_ReposRoot, svn::DepthEmpty, aRev, aRev));
0408         ;
0409         if (e.count() < 1 || e[0].reposRoot().isEmpty()) {
0410             return aRev;
0411         }
0412         return e[0].revision();
0413     }
0414     query.prepare(QStringLiteral("select revision from logentries where date<? order by revision desc"));
0415     query.bindValue(0, Q_LLONG(aRev.date()));
0416     if (query.exec() && query.next()) {
0417         return query.value(0).toInt();
0418     }
0419     // not found...
0420     if (noNetwork) {
0421         return svn::Revision::UNDEFINED;
0422     }
0423     const svn::InfoEntries e = (m_Client->info(m_ReposRoot, svn::DepthEmpty, svn::Revision::HEAD, svn::Revision::HEAD));
0424     ;
0425     if (e.count() < 1 || e[0].reposRoot().isEmpty()) {
0426         return svn::Revision::UNDEFINED;
0427     }
0428     return e[0].revision();
0429 }
0430 
0431 /*!
0432     \fn svn::cache::ReposLog::insertLogEntry(const svn::LogEntry&)
0433  */
0434 bool svn::cache::ReposLog::_insertLogEntry(const svn::LogEntry &aEntry)
0435 {
0436     qlonglong j = aEntry.revision;
0437     static const QLatin1String qEntry("insert into logentries (revision,date,author,message) values (?,?,?,?)");
0438     static const QLatin1String qPathes("insert into changeditems (revision,changeditem,action,copyfrom,copyfromrev) values (?,?,?,?,?)");
0439     QSqlQuery _q(QString(), m_Database);
0440     _q.prepare(qEntry);
0441     _q.bindValue(0, j);
0442     _q.bindValue(1, aEntry.date);
0443     _q.bindValue(2, aEntry.author);
0444     _q.bindValue(3, aEntry.message);
0445     if (!_q.exec()) {
0446         // qDebug("Could not insert values: %s",_q.lastError().text().toUtf8().data());
0447         // qDebug() << _q.lastQuery();
0448         throw svn::cache::DatabaseException(
0449             QStringLiteral("_insertLogEntry_0: Could not insert values: %1, %2").arg(_q.lastError().text(), _q.lastError().nativeErrorCode()));
0450     }
0451     _q.prepare(qPathes);
0452     for (const LogChangePathEntry &cp : aEntry.changedPaths) {
0453         _q.bindValue(0, j);
0454         _q.bindValue(1, cp.path);
0455         _q.bindValue(2, QString(QLatin1Char(cp.action)));
0456         _q.bindValue(3, cp.copyFromPath);
0457         _q.bindValue(4, Q_LLONG(cp.copyFromRevision));
0458         if (!_q.exec()) {
0459             // qDebug("Could not insert values: %s",_q.lastError().text().toUtf8().data());
0460             // qDebug() << _q.lastQuery();
0461             throw svn::cache::DatabaseException(QStringLiteral("Could not insert values: %1, %2").arg(_q.lastError().text(), _q.lastError().nativeErrorCode()));
0462         }
0463     }
0464     if (!aEntry.m_MergedInRevisions.isEmpty()) {
0465         static const QLatin1String qMerges("insert into mergeditems(revision,mergeditems) values(?,?)");
0466         _q.prepare(qMerges);
0467         QByteArray _merges;
0468         QBuffer buffer(&_merges);
0469         buffer.open(QIODevice::ReadWrite);
0470         QDataStream af(&buffer);
0471         af << aEntry.m_MergedInRevisions;
0472         buffer.close();
0473         _q.bindValue(0, j);
0474         _q.bindValue(1, _merges);
0475         if (!_q.exec()) {
0476             // qDebug("Could not insert values: %s",_q.lastError().text().toUtf8().data());
0477             // qDebug() << _q.lastQuery();
0478             throw svn::cache::DatabaseException(QStringLiteral("Could not insert values: %1, %2").arg(_q.lastError().text(), _q.lastError().nativeErrorCode()));
0479         }
0480     }
0481     return true;
0482 }
0483 
0484 bool svn::cache::ReposLog::insertLogEntry(const svn::LogEntry &aEntry)
0485 {
0486     DatabaseLocker l(&m_Database);
0487     if (!_insertLogEntry(aEntry)) {
0488         return false;
0489     }
0490     l.commit();
0491     return true;
0492 }
0493 
0494 /*!
0495     \fn svn::cache::ReposLog::log(const svn::Path&,const svn::Revision&start, const svn::Revision&end,const svn::Revision&peg,svn::LogEntriesMap&target, bool
0496    strictNodeHistory,int limit))
0497  */
0498 bool svn::cache::ReposLog::log(const svn::Path &what,
0499                                const svn::Revision &_start,
0500                                const svn::Revision &_end,
0501                                const svn::Revision &_peg,
0502                                svn::LogEntriesMap &target,
0503                                bool strictNodeHistory,
0504                                int limit)
0505 {
0506     Q_UNUSED(strictNodeHistory);
0507     static const QLatin1String s_q(
0508         "select logentries.revision,logentries.author,logentries.date,logentries.message from logentries where logentries.revision in (select "
0509         "changeditems.revision from changeditems where (changeditems.changeditem='%1' or changeditems.changeditem GLOB '%2/*') %3 GROUP BY "
0510         "changeditems.revision) ORDER BY logentries.revision DESC");
0511 
0512     static const QLatin1String s_e("select changeditem,action,copyfrom,copyfromrev from changeditems where changeditems.revision='%1'");
0513     static const QLatin1String s_m("select mergeditems from mergeditems where mergeditems.revision='%1'");
0514 
0515     svn::Revision peg = date2numberRev(_peg, true);
0516     QString query_string =
0517         QString(s_q).arg(what.native(), what.native(), (peg == svn::Revision::UNDEFINED ? QString() : QStringLiteral(" AND revision<=%1").arg(peg.revnum())));
0518     if (peg == svn::Revision::UNDEFINED) {
0519         peg = latestCachedRev();
0520     }
0521     if (!itemExists(peg, what)) {
0522         throw svn::cache::DatabaseException(QStringLiteral("Entry '%1' does not exists at revision %2").arg(what.native(), peg.toString()));
0523     }
0524     if (limit > 0) {
0525         query_string += QStringLiteral(" LIMIT %1").arg(limit);
0526     }
0527     QSqlQuery _q(m_Database);
0528     QSqlQuery _q2(m_Database);
0529     _q.setForwardOnly(true);
0530     _q.prepare(query_string);
0531     if (!_q.exec()) {
0532         // qDebug("Could not select values: %s",_q.lastError().text().toUtf8().data());
0533         // qDebug() << _q.lastQuery();
0534         throw svn::cache::DatabaseException(QStringLiteral("Could not select values: %1, %2").arg(_q.lastError().text(), _q.lastError().nativeErrorCode()));
0535     }
0536     while (_q.next()) {
0537         Q_LLONG revision = _q.value(0).toLongLong();
0538         target[revision].revision = revision;
0539         target[revision].author = _q.value(1).toString();
0540         target[revision].date = _q.value(2).toLongLong();
0541         target[revision].message = _q.value(3).toString();
0542         query_string = QString(s_e).arg(revision);
0543         _q2.setForwardOnly(true);
0544         _q2.prepare(query_string);
0545         if (!_q2.exec()) {
0546             // qDebug("Could not select values: %s",_q2.lastError().text().toUtf8().data());
0547         } else {
0548             while (_q2.next()) {
0549                 target[revision].changedPaths.push_back(
0550                     LogChangePathEntry(_q2.value(0).toString(), _q2.value(1).toChar().toLatin1(), _q2.value(2).toString(), _q2.value(3).toLongLong()));
0551             }
0552         }
0553         query_string = QString(s_m).arg(revision);
0554         _q2.prepare(query_string);
0555         if (!_q2.exec()) {
0556             // qDebug("Could not select values: %s",_q2.lastError().text().toUtf8().data());
0557         } else {
0558             if (_q2.next()) {
0559                 QByteArray byteArray = _q2.value(0).toByteArray();
0560                 QBuffer buffer(&byteArray);
0561                 QDataStream in(&buffer);
0562                 in >> target[revision].m_MergedInRevisions;
0563             }
0564         }
0565     }
0566     return true;
0567 }
0568 
0569 /*!
0570     \fn svn::cache::ReposLog::itemExists(const svn::Revision&,const QString&)
0571  */
0572 bool svn::cache::ReposLog::itemExists(const svn::Revision &peg, const svn::Path &path)
0573 {
0574     /// @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
0575     /// simple: it exists if we found it.
0576 
0577     Q_UNUSED(peg);
0578     Q_UNUSED(path);
0579 
0580 #if 0
0581     static QString _s1("select revision from changeditems where changeditem='%1' and action='A' and revision<=%2 order by revision desc limit 1");
0582     QSqlQuery _q(QString(), m_Database);
0583     QString query_string = QString(_s1).arg(path.native()).arg(peg.revnum());
0584     if (!_q.exec(query_string)) {
0585         //qDebug("Could not select values: %s",_q.lastError().text().toUtf8().data());
0586         //qDebug(_q.lastQuery().toUtf8().data());
0587         throw svn::cache::DatabaseException(QString("Could not select values: ") + _q.lastError().text(), _q.lastError().number());
0588     }
0589     //qDebug(_q.lastQuery().toUtf8().data());
0590 
0591     svn::Path _p = path;
0592     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");
0593     QStringList p_list;
0594     while (_p.length() > 0) {
0595         p_list.append(QString("'%1'").arg(_p.native()));
0596         _p.removeLast();
0597     }
0598     query_string = QString(_s2).arg(p_list.join(",")).arg();
0599 #endif
0600     return true;
0601 }
0602 
0603 bool svn::cache::ReposLog::isValid() const
0604 {
0605     if (!m_Database.isValid()) {
0606         m_Database = LogCache::self()->reposDb(m_ReposRoot);
0607         if (!m_Database.isValid()) {
0608             return false;
0609         }
0610     }
0611     return true;
0612 }
0613 
0614 void svn::cache::ReposLog::cleanLogEntries()
0615 {
0616     if (!isValid()) {
0617         return;
0618     }
0619     DatabaseLocker l(&m_Database);
0620     QSqlQuery _q(QString(), m_Database);
0621     if (!_q.exec(QStringLiteral("delete from logentries"))) {
0622         return;
0623     }
0624     if (!_q.exec(QStringLiteral("delete from changeditems"))) {
0625         return;
0626     }
0627     if (!_q.exec(QStringLiteral("delete from mergeditems"))) {
0628         return;
0629     }
0630 
0631     l.commit();
0632     _q.exec(QStringLiteral("vacuum"));
0633 }