summaryrefslogtreecommitdiffstats
path: root/src/svnqt/cache
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-03-15 17:32:48 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-03-15 17:32:48 +0000
commite2f541c98dfa4081fa3ab3d28f08ea2309281884 (patch)
treecb721a55bc88753ddeb9754dc98ef45e2850ce30 /src/svnqt/cache
downloadtdesvn-e2f541c98dfa4081fa3ab3d28f08ea2309281884.tar.gz
tdesvn-e2f541c98dfa4081fa3ab3d28f08ea2309281884.zip
Added KDE3 version of kdesvn
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/kdesvn@1103685 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src/svnqt/cache')
-rw-r--r--src/svnqt/cache/DatabaseException.cpp12
-rw-r--r--src/svnqt/cache/DatabaseException.hpp35
-rw-r--r--src/svnqt/cache/LogCache.cpp468
-rw-r--r--src/svnqt/cache/LogCache.hpp41
-rw-r--r--src/svnqt/cache/ReposLog.cpp535
-rw-r--r--src/svnqt/cache/ReposLog.hpp70
-rw-r--r--src/svnqt/cache/sqlite3/README32
-rw-r--r--src/svnqt/cache/sqlite3/qsql_sqlite3.cpp485
-rw-r--r--src/svnqt/cache/sqlite3/qsql_sqlite3.h90
-rw-r--r--src/svnqt/cache/sqlite3/qsqlcachedresult.cpp260
-rw-r--r--src/svnqt/cache/sqlite3/qsqlcachedresult.h81
-rw-r--r--src/svnqt/cache/test/CMakeLists.txt19
-rw-r--r--src/svnqt/cache/test/sqlite.cpp111
-rw-r--r--src/svnqt/cache/test/testconfig.h.in8
14 files changed, 2247 insertions, 0 deletions
diff --git a/src/svnqt/cache/DatabaseException.cpp b/src/svnqt/cache/DatabaseException.cpp
new file mode 100644
index 0000000..85812ab
--- /dev/null
+++ b/src/svnqt/cache/DatabaseException.cpp
@@ -0,0 +1,12 @@
+#include "DatabaseException.hpp"
+
+/*!
+ \fn svn::cache::DatabaseException::DatabaseException(const QString&msg,int aNumber)throw()
+ */
+svn::cache::DatabaseException::DatabaseException(const QString&msg,int aNumber)throw()
+ : Exception(msg),m_number(aNumber)
+{
+ if (aNumber>-1) {
+ setMessage(QString("(Code %1) %2").arg(aNumber).arg(msg));
+ }
+}
diff --git a/src/svnqt/cache/DatabaseException.hpp b/src/svnqt/cache/DatabaseException.hpp
new file mode 100644
index 0000000..85e5ce4
--- /dev/null
+++ b/src/svnqt/cache/DatabaseException.hpp
@@ -0,0 +1,35 @@
+#ifndef _DATABASE_EXCEPTION_HPP
+#define _DATABASE_EXCEPTION_HPP
+
+#include "svnqt/exception.hpp"
+
+namespace svn
+{
+namespace cache
+{
+
+class SVNQT_EXPORT DatabaseException:public svn::Exception
+{
+ private:
+ DatabaseException()throw();
+ int m_number;
+
+ public:
+ DatabaseException(const QString&msg)throw()
+ : Exception(msg),m_number(-1)
+ {}
+
+ DatabaseException(const DatabaseException&src)throw()
+ : Exception(src.msg()),m_number(src.number())
+ {}
+ DatabaseException(const QString&msg,int aNumber)throw();
+ virtual ~DatabaseException()throw(){}
+ int number() const
+ {
+ return m_number;
+ }
+};
+
+}
+}
+#endif
diff --git a/src/svnqt/cache/LogCache.cpp b/src/svnqt/cache/LogCache.cpp
new file mode 100644
index 0000000..6356c6f
--- /dev/null
+++ b/src/svnqt/cache/LogCache.cpp
@@ -0,0 +1,468 @@
+#include "LogCache.hpp"
+
+#include <qdir.h>
+#include <qsql.h>
+#include <qsqldatabase.h>
+#if QT_VERSION < 0x040000
+#include <qthreadstorage.h>
+#else
+#include <QMutex>
+#include <QThreadStorage>
+#include <QSqlError>
+#include <QSqlQuery>
+#include <QVariant>
+#endif
+#include <qmap.h>
+
+#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(){
+#if QT_VERSION < 0x040000
+ m_DB=0;
+#else
+ m_DB=QSqlDatabase();
+#endif
+ }
+ ~ThreadDBStore(){
+#if QT_VERSION < 0x040000
+ m_DB=0;
+#else
+ m_DB=QSqlDatabase();
+#endif
+ QSqlDatabase::removeDatabase(key);
+ QMap<QString,QString>::Iterator it;
+ for (it=reposCacheNames.begin();it!=reposCacheNames.end();++it) {
+#if QT_VERSION < 0x040000
+ QSqlDatabase::removeDatabase(it.data());
+#else
+ QSqlDatabase::removeDatabase(it.value());
+#endif
+ }
+ }
+
+ QDataBase m_DB;
+ QString key;
+ QMap<QString,QString> reposCacheNames;
+};
+
+class LogCacheData
+{
+
+protected:
+ QMutex m_singleDbMutex;
+
+public:
+ LogCacheData(){}
+ ~LogCacheData(){
+ if (m_mainDB.hasLocalData()) {
+ m_mainDB.setLocalData(0L);
+ }
+ }
+
+ bool checkReposDb(QDataBase aDb)
+ {
+#if QT_VERSION < 0x040000
+ if (!aDb) {
+ return false;
+ }
+ if (!aDb->open()) {
+ return false;
+ }
+#else
+ if (!aDb.open()) {
+ return false;
+ }
+#endif
+
+ QSqlQuery _q(QString::null, aDb);
+#if QT_VERSION < 0x040000
+ QStringList list = aDb->tables();
+#else
+ QStringList list = aDb.tables();
+#endif
+
+#if QT_VERSION < 0x040000
+ if (list.find("logentries")==list.end()) {
+ aDb->transaction();
+#else
+ if (list.indexOf("logentries")==-1) {
+ aDb.transaction();
+#endif
+ _q.exec("CREATE TABLE \"logentries\" (\"revision\" INTEGER UNIQUE,\"date\" INTEGER,\"author\" TEXT, \"message\" TEXT)");
+#if QT_VERSION < 0x040000
+ aDb->commit();
+#else
+ aDb.commit();
+#endif
+ }
+#if QT_VERSION < 0x040000
+ if (list.find("changeditems")==list.end()) {
+ aDb->transaction();
+#else
+ if (list.indexOf("changeditems")==-1) {
+ aDb.transaction();
+#endif
+ _q.exec("CREATE TABLE \"changeditems\" (\"revision\" INTEGER,\"changeditem\" TEXT,\"action\" TEXT,\"copyfrom\" TEXT,\"copyfromrev\" INTEGER, PRIMARY KEY(revision,changeditem,action))");
+#if QT_VERSION < 0x040000
+ aDb->commit();
+#else
+ aDb.commit();
+#endif
+ }
+#if QT_VERSION < 0x040000
+ list = aDb->tables();
+ if (list.find("logentries")==list.end() || list.find("changeditems")==list.end()) {
+#else
+ list = aDb.tables();
+ if (list.indexOf("logentries")==-1 || list.indexOf("changeditems")==-1) {
+#endif
+ return false;
+ }
+ return true;
+ }
+
+ QString createReposDB(const svn::Path&reposroot) {
+ QMutexLocker locker( &m_singleDbMutex );
+
+ QDataBase _mdb = getMainDB();
+
+ QSqlQuery query1(QString::null,_mdb);
+ QString q("insert into "+QString(SQLMAINTABLE)+" (reposroot) VALUES('"+reposroot+"')");
+#if QT_VERSION < 0x040000
+ _mdb->transaction();
+#else
+ _mdb.transaction();
+#endif
+
+ query1.exec(q);
+#if QT_VERSION < 0x040000
+ _mdb->commit();
+#else
+ _mdb.commit();
+#endif
+ QSqlQuery query(QString::null,_mdb);
+ query.prepare(s_reposSelect);
+ query.bindValue(0,reposroot.native());
+ query.exec();
+ QString db;
+#if QT_VERSION < 0x040000
+ if (query.lastError().type()==QSqlError::None && query.next()) {
+#else
+ if (query.lastError().type()==QSqlError::NoError && query.next()) {
+#endif
+ db = query.value(0).toString();
+ }
+ else {
+ qDebug("Error select_01: %s (%s)",query.lastError().text().TOUTF8().data(),
+ query.lastQuery().TOUTF8().data());
+ }
+ if (!db.isEmpty()) {
+ QString fulldb = m_BasePath+"/"+db+".db";
+ QDataBase _db = QSqlDatabase::addDatabase(SQLTYPE,"tmpdb");
+#if QT_VERSION < 0x040000
+ _db->setDatabaseName(fulldb);
+#else
+ _db.setDatabaseName(fulldb);
+#endif
+ if (!checkReposDb(_db)) {
+ }
+ QSqlDatabase::removeDatabase("tmpdb");
+ }
+ return db;
+ }
+
+ QDataBase getReposDB(const svn::Path&reposroot) {
+#if QT_VERSION < 0x040000
+ if (!getMainDB()) {
+ return 0;
+#else
+ if (!getMainDB().isValid()) {
+ return QDataBase();
+#endif
+ }
+ bool checkDone = false;
+ // make sure path is correct eg. without traling slashes.
+ QString dbFile;
+ QSqlQuery c(QString::null,getMainDB());
+ c.prepare(s_reposSelect);
+ c.bindValue(0,reposroot.native());
+ c.exec();
+
+#if QT_VERSION < 0x040000
+ //qDebug("Check for path: "+reposroot.native());
+#endif
+
+ // only the first one
+ if ( c.next() ) {
+#if QT_VERSION < 0x040000
+/* qDebug( c.value(0).toString() + ": " +
+ c.value(0).toString() );*/
+#endif
+ dbFile = c.value(0).toString();
+ }
+ if (dbFile.isEmpty()) {
+ dbFile = createReposDB(reposroot);
+ if (dbFile.isEmpty()) {
+#if QT_VERSION < 0x040000
+ return 0;
+#else
+ return QSqlDatabase();
+#endif
+ }
+ checkDone=true;
+ }
+ if (m_mainDB.localData()->reposCacheNames.find(dbFile)!=m_mainDB.localData()->reposCacheNames.end()) {
+ return QSqlDatabase::database(m_mainDB.localData()->reposCacheNames[dbFile]);
+ }
+ int i = 0;
+ QString _key = dbFile;
+ while (QSqlDatabase::contains(_key)) {
+ _key = QString("%1-%2").arg(dbFile).arg(i++);
+ }
+// qDebug("The repository key is now: %s",_key.TOUTF8().data());
+ QDataBase _db = QSqlDatabase::addDatabase(SQLTYPE,_key);
+#if QT_VERSION < 0x040000
+ if (!_db) {
+ return 0;
+ }
+#endif
+ QString fulldb = m_BasePath+"/"+dbFile+".db";
+#if QT_VERSION < 0x040000
+ _db->setDatabaseName(fulldb);
+#else
+ _db.setDatabaseName(fulldb);
+#endif
+// qDebug("try database open %s",fulldb.TOUTF8().data());
+ if (!checkReposDb(_db)) {
+ qDebug("no DB opened");
+#if QT_VERSION < 0x040000
+ _db = 0;
+#else
+ _db = QSqlDatabase();
+#endif
+ } else {
+ qDebug("Insert into map");
+ m_mainDB.localData()->reposCacheNames[dbFile]=_key;
+ }
+ return _db;
+ }
+
+ QDataBase getMainDB()const
+ {
+ if (!m_mainDB.hasLocalData()) {
+ unsigned i=0;
+ QString _key = SQLMAIN;
+ while (QSqlDatabase::contains(_key)) {
+ _key.sprintf("%s-%i",SQLMAIN,i++);
+ }
+ qDebug("The key is now: %s",_key.TOUTF8().data());
+
+ QDataBase db = QSqlDatabase::addDatabase(SQLTYPE,_key);
+#if QT_VERSION < 0x040000
+ db->setDatabaseName(m_BasePath+"/maindb.db");
+ if (!db->open()) {
+#else
+ db.setDatabaseName(m_BasePath+"/maindb.db");
+ if (!db.open()) {
+#endif
+#if QT_VERSION < 0x040000
+ qWarning("Failed to open main database: " + db->lastError().text());
+#endif
+ } 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 {
+#if QT_VERSION < 0x040000
+ return 0;
+#else
+ return QSqlDatabase();
+#endif
+ }
+ }
+ QString m_BasePath;
+
+ mutable QThreadStorage<ThreadDBStore*> m_mainDB;
+
+ static const QString s_reposSelect;
+};
+
+
+QString LogCache::s_CACHE_FOLDER="logcache";
+const QString LogCacheData::s_reposSelect=QString("SELECT id from ")+QString(SQLMAINTABLE)+QString(" where reposroot=? ORDER by id DESC");
+
+/*!
+ \fn svn::cache::LogCache::LogCache()
+ */
+LogCache::LogCache()
+{
+ m_BasePath = QDir::HOMEDIR()+"/.svnqt";
+ setupCachePath();
+}
+
+LogCache::LogCache(const QString&aBasePath)
+{
+ if (mSelf) {
+ delete mSelf;
+ }
+ mSelf=this;
+ if (aBasePath.isEmpty()) {
+ m_BasePath=QDir::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;
+ QDir 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 (!QSqlDatabase::isDriverAvailable(SQLTYPE)) {
+ QSqlDatabase::registerSqlDriver(SQLTYPE,new QSqlDriverCreator<QSQLite3Driver>);
+ }
+#endif
+ QDataBase mainDB = m_CacheData->getMainDB();
+#if QT_VERSION < 0x040000
+ if (!mainDB || !mainDB->open()) {
+ qWarning("Failed to open main database: " + (mainDB?mainDB->lastError().text():"No database object."));
+#else
+ if (!mainDB.isValid()) {
+ qWarning("Failed to open main database.");
+#endif
+ } else {
+ QSqlQuery q(QString::null, mainDB);
+#if QT_VERSION < 0x040000
+ mainDB->transaction();
+#else
+ mainDB.transaction();
+#endif
+ if (!q.exec("CREATE TABLE IF NOT EXISTS \""+QString(SQLMAINTABLE)+"\" (\"reposroot\" TEXT,\"id\" INTEGER PRIMARY KEY NOT NULL);")) {
+#if QT_VERSION < 0x040000
+ qWarning("Failed create main database: " + mainDB->lastError().text());
+#endif
+ }
+#if QT_VERSION < 0x040000
+ mainDB->commit();
+#else
+ mainDB.commit();
+#endif
+ }
+}
+
+}
+}
+
+
+/*!
+ \fn svn::cache::LogCache::self()
+ */
+svn::cache::LogCache* svn::cache::LogCache::self()
+{
+ if (!mSelf) {
+ mSelf=new LogCache();
+ }
+ return mSelf;
+}
+
+
+/*!
+ \fn svn::cache::LogCache::reposDb()
+ */
+QDataBase 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 QString s_q(QString("select \"reposroot\" from ")+QString(SQLMAINTABLE)+QString("order by reposroot"));
+ QDataBase mainDB = m_CacheData->getMainDB();
+ QStringList _res;
+#if QT_VERSION < 0x040000
+ if (!mainDB || !mainDB->open()) {
+#else
+ if (!mainDB.isValid()) {
+#endif
+ qWarning("Failed to open main database.");
+ return _res;
+ }
+ QSqlQuery cur(QString::null,mainDB);
+ cur.prepare(s_q);
+ if (!cur.exec()) {
+ qDebug(cur.lastError().text().TOUTF8().data());
+ throw svn::cache::DatabaseException(QString("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
+{
+ QDataBase mainDB = m_CacheData->getMainDB();
+#if QT_VERSION < 0x040000
+ if (!mainDB || !mainDB->open()) {
+#else
+ if (!mainDB.isValid()) {
+#endif
+ return false;
+ }
+ return true;
+}
diff --git a/src/svnqt/cache/LogCache.hpp b/src/svnqt/cache/LogCache.hpp
new file mode 100644
index 0000000..9e76697
--- /dev/null
+++ b/src/svnqt/cache/LogCache.hpp
@@ -0,0 +1,41 @@
+#ifndef _LOG_CACHE_HPP
+#define _LOG_CACHE_HPP
+
+#include <qstring.h>
+#include <qdir.h>
+
+#include "svnqt/svnqt_defines.hpp"
+#include "svnqt/shared_pointer.hpp"
+
+namespace svn {
+ namespace cache {
+
+ class LogCacheData;
+
+ class SVNQT_EXPORT LogCache
+ {
+ private:
+ svn::SharedPointer<LogCacheData> m_CacheData;
+
+ protected:
+ LogCache();
+ static LogCache* mSelf;
+ QString m_BasePath;
+ static QString s_CACHE_FOLDER;
+ void setupCachePath();
+ void setupMainDb();
+
+ public:
+ ///! should used for testing only!
+ LogCache(const QString&aBasePath);
+ virtual ~LogCache();
+ static LogCache* self();
+ QDataBase reposDb(const QString&aRepository);
+ QStringList cachedRepositories()const;
+
+ bool valid()const;
+ };
+ }
+}
+
+#endif
diff --git a/src/svnqt/cache/ReposLog.cpp b/src/svnqt/cache/ReposLog.cpp
new file mode 100644
index 0000000..89be2d0
--- /dev/null
+++ b/src/svnqt/cache/ReposLog.cpp
@@ -0,0 +1,535 @@
+#include "ReposLog.hpp"
+
+#include "LogCache.hpp"
+#include "svnqt/info_entry.hpp"
+#include "svnqt/svnqttypes.hpp"
+#include "svnqt/client.hpp"
+#include "svnqt/context_listener.hpp"
+#include "svnqt/cache/DatabaseException.hpp"
+
+#include <qsqldatabase.h>
+
+#if QT_VERSION < 0x040000
+#else
+#include <QSqlError>
+#include <QSqlQuery>
+#include <QVariant>
+#define Q_LLONG qlonglong
+#endif
+
+/*!
+ \fn svn::cache::ReposLog::ReposLog(svn::Client*aClient,const QString&)
+ */
+svn::cache::ReposLog::ReposLog(svn::Client*aClient,const QString&aRepository)
+ :m_Client(),
+#if QT_VERSION < 0x040000
+ m_Database(0),
+#else
+ m_Database(),
+#endif
+ m_ReposRoot(aRepository),m_latestHead(svn::Revision::UNDEFINED)
+{
+ m_Client=aClient;
+ ContextP ctx = m_Client->getContext();
+ 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 QT_VERSION < 0x040000
+ if (!m_Database) {
+#else
+ if (!m_Database.isValid()) {
+#endif
+ m_Database = LogCache::self()->reposDb(m_ReposRoot);
+#if QT_VERSION < 0x040000
+ if (!m_Database) {
+#else
+ if (!m_Database.isValid()) {
+#endif
+ 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);
+ 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 QT_VERSION < 0x040000
+ if (!m_Database) {
+#else
+ if (!m_Database.isValid()) {
+#endif
+ m_Database = LogCache::self()->reposDb(m_ReposRoot);
+#if QT_VERSION < 0x040000
+ if (!m_Database) {
+#else
+ if (!m_Database.isValid()) {
+#endif
+ return svn::Revision::UNDEFINED;
+ }
+ }
+ QString q("select revision from 'logentries' order by revision DESC limit 1");
+ QSqlQuery _q(QString::null, m_Database);
+ if (!_q.exec(q)) {
+ qDebug(_q.lastError().text().TOUTF8().data());
+ 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().TOUTF8().data());
+ return svn::Revision::UNDEFINED;
+ }
+ return _r;
+}
+
+bool svn::cache::ReposLog::checkFill(svn::Revision&start,svn::Revision&end,bool checkHead)
+{
+#if QT_VERSION < 0x040000
+ if (!m_Database) {
+#else
+ if (!m_Database.isValid()) {
+#endif
+ m_Database = LogCache::self()->reposDb(m_ReposRoot);
+#if QT_VERSION < 0x040000
+ if (!m_Database) {
+#else
+ if (!m_Database.isValid()) {
+#endif
+ 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.");
+ if (!m_Client->log(m_ReposRoot,_rstart,_rend,_internal,svn::Revision::UNDEFINED,true,false)) {
+ return false;
+ }
+ LogEntriesMap::ConstIterator it=_internal.begin();
+
+ for (;it!=_internal.end();++it) {
+ _insertLogEntry((*it));
+ if (cp && cp->getListener()) {
+ //cp->getListener()->contextProgress(++icount,_internal.size());
+ if (cp->getListener()->contextCancel()) {
+ throw DatabaseException(QString("Could not retrieve values: User cancel."));
+ }
+ }
+ }
+ }
+ 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)
+{
+ 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();
+ }
+ static QString sCount("select count(*) from logentries where revision<=? and revision>=?");
+ static QString sEntry("select revision,author,date,message from logentries where revision<=? and revision>=?");
+ static QString sItems("select changeditem,action,copyfrom,copyfromrev from changeditems where revision=?");
+
+ QSqlQuery bcount(QString::null,m_Database);
+ bcount.prepare(sCount);
+
+ QSqlQuery bcur(QString::null,m_Database);
+ bcur.prepare(sEntry);
+
+ QSqlQuery cur(QString::null,m_Database);
+ cur.prepare(sItems);
+
+ bcount.bindValue(0,Q_LLONG(end.revnum()));
+ bcount.bindValue(1,Q_LLONG(start.revnum()));
+ if (!bcount.exec()) {
+ qDebug(bcount.lastError().text().TOUTF8().data());
+ throw svn::cache::DatabaseException(QString("Could not retrieve count: ")+bcount.lastError().text());
+ return false;
+ }
+ bcount.next();
+ if (bcount.value(0).toLongLong()<1) {
+ // we didn't found logs with this parameters
+ return false;
+ }
+
+ bcur.bindValue(0,Q_LLONG(end.revnum()));
+ bcur.bindValue(1,Q_LLONG(start.revnum()));
+
+ if (!bcur.exec()) {
+ qDebug(bcur.lastError().text().TOUTF8().data());
+ throw svn::cache::DatabaseException(QString("Could not retrieve values: ")+bcur.lastError().text());
+ return false;
+ }
+ Q_LLONG revision;
+ while(bcur.next()) {
+ revision = bcur.value(0).toLongLong();
+ cur.bindValue(0,revision);
+ if (!cur.exec()) {
+ qDebug(cur.lastError().text().TOUTF8().data());
+ throw svn::cache::DatabaseException(QString("Could not retrieve values: ")+cur.lastError().text()
+ ,cur.lastError().number());
+ 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();
+#if QT_VERSION < 0x040000
+ lcp.action=ac[0].latin1();
+#else
+ lcp.action=ac[0].toLatin1();
+#endif
+ 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(QString("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 QT_VERSION < 0x040000
+ if (!m_Database) {
+#else
+ if (!m_Database.isValid()) {
+#endif
+ return svn::Revision::UNDEFINED;
+ }
+ static QString _q("select revision from logentries where date<? order by revision desc");
+ QSqlQuery query("select revision,date from logentries order by revision desc limit 1",m_Database);
+
+#if QT_VERSION < 0x040000
+ if (query.lastError().type()!=QSqlError::None) {
+#else
+ if (query.lastError().type()!=QSqlError::NoError) {
+#endif
+ qDebug(query.lastError().text().TOUTF8().data());
+ }
+ 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));;
+ if (e.count()<1||e[0].reposRoot().isEmpty()) {
+ return aRev;
+ }
+ return e[0].revision();
+ }
+ query.prepare(_q);
+ query.bindValue(0,Q_LLONG(aRev.date()));
+ query.exec();
+#if QT_VERSION < 0x040000
+ if (query.lastError().type()!=QSqlError::None) {
+#else
+ if (query.lastError().type()!=QSqlError::NoError) {
+#endif
+ qDebug(query.lastError().text().TOUTF8().data());
+ }
+ if (query.next()) {
+ return query.value(0).toInt();
+ }
+ // not found...
+ if (noNetwork) {
+ return svn::Revision::UNDEFINED;
+ }
+ 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)
+{
+ QSqlRecord *buffer;
+
+#if QT_VERSION < 0x040000
+ m_Database->transaction();
+ Q_LLONG j = aEntry.revision;
+#else
+ m_Database.transaction();
+ qlonglong j = aEntry.revision;
+#endif
+ static QString qEntry("insert into logentries (revision,date,author,message) values (?,?,?,?)");
+ static QString qPathes("insert into changeditems (revision,changeditem,action,copyfrom,copyfromrev) values (?,?,?,?,?)");
+ QSqlQuery _q(QString::null,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()) {
+#if QT_VERSION < 0x040000
+ m_Database->rollback();
+#else
+ m_Database.rollback();
+#endif
+ qDebug("Could not insert values: %s",_q.lastError().text().TOUTF8().data());
+ qDebug(_q.lastQuery().TOUTF8().data());
+ throw svn::cache::DatabaseException(QString("Could not insert values: ")+_q.lastError().text(),_q.lastError().number());
+ }
+ _q.prepare(qPathes);
+ svn::LogChangePathEntries::ConstIterator cpit = aEntry.changedPaths.begin();
+ for (;cpit!=aEntry.changedPaths.end();++cpit){
+ _q.bindValue(0,j);
+ _q.bindValue(1,(*cpit).path);
+ _q.bindValue(2,QString(QChar((*cpit).action)));
+ _q.bindValue(3,(*cpit).copyFromPath);
+ _q.bindValue(4,Q_LLONG((*cpit).copyFromRevision));
+ if (!_q.exec()) {
+#if QT_VERSION < 0x040000
+ m_Database->rollback();
+#else
+ m_Database.rollback();
+#endif
+ qDebug("Could not insert values: %s",_q.lastError().text().TOUTF8().data());
+ qDebug(_q.lastQuery().TOUTF8().data());
+ throw svn::cache::DatabaseException(QString("Could not insert values: ")+_q.lastError().text(),_q.lastError().number());
+ }
+ }
+#if QT_VERSION < 0x040000
+ m_Database->commit();
+#else
+ m_Database.commit();
+#endif
+ return true;
+}
+
+bool svn::cache::ReposLog::insertLogEntry(const svn::LogEntry&aEntry)
+{
+ return _insertLogEntry(aEntry);
+}
+
+
+/*!
+ \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)
+{
+ static QString 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 QString s_e("select changeditem,action,copyfrom,copyfromrev from changeditems where changeditems.revision='%1'");
+
+ svn::Revision peg = date2numberRev(_peg,true);
+ svn::Revision end = date2numberRev(_end,true);
+ svn::Revision start = date2numberRev(_start,true);
+ QString query_string = QString(s_q).arg(what.native()).arg(what.native()).arg((peg==svn::Revision::UNDEFINED?"":QString(" AND revision<=%1").arg(peg.revnum())));
+ if (peg==svn::Revision::UNDEFINED) {
+ peg = latestCachedRev();
+ }
+ if (!itemExists(peg,what)) {
+ throw svn::cache::DatabaseException(QString("Entry '%1' does not exists at revision %2").arg(what.native()).arg(peg.toString()));
+ }
+ if (limit>0) {
+ query_string+=QString(" LIMIT %1").arg(limit);
+ }
+ QSqlQuery _q(QString::null,m_Database);
+ QSqlQuery _q2(QString::null,m_Database);
+ _q.prepare(query_string);
+ if (!_q.exec()) {
+ 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());
+ }
+ 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=s_e.arg(revision);
+ _q2.prepare(query_string);
+ if (!_q2.exec()) {
+ qDebug("Could not select values: %s",_q2.lastError().text().TOUTF8().data());
+ } else {
+ while (_q2.next()) {
+#if QT_VERSION < 0x040000
+ target[revision].changedPaths.push_back (
+ LogChangePathEntry (_q2.value(0).toString(),
+ _q2.value(1).toString()[0],
+ _q2.value(2).toString(),
+ _q2.value(3).toLongLong()
+ )
+ );
+#else
+ target[revision].changedPaths.push_back (
+ LogChangePathEntry (_q2.value(0).toString(),
+ _q2.value(1).toChar().toLatin1(),
+ _q2.value(2).toString(),
+ _q2.value(3).toLongLong()
+ )
+ );
+#endif
+ }
+ }
+
+ }
+ 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.
+
+
+#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::null,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 QT_VERSION < 0x040000
+ if (!m_Database) {
+#else
+ if (!m_Database.isValid()) {
+#endif
+ m_Database = LogCache::self()->reposDb(m_ReposRoot);
+#if QT_VERSION < 0x040000
+ if (!m_Database) {
+#else
+ if (!m_Database.isValid()) {
+#endif
+ return false;
+ }
+ }
+ return true;
+}
diff --git a/src/svnqt/cache/ReposLog.hpp b/src/svnqt/cache/ReposLog.hpp
new file mode 100644
index 0000000..e81a5c9
--- /dev/null
+++ b/src/svnqt/cache/ReposLog.hpp
@@ -0,0 +1,70 @@
+#ifndef _REPOS_LOG_HPP
+#define _REPOS_LOG_HPP
+
+#include "svnqt/svnqt_defines.hpp"
+#include "svnqt/svnqttypes.hpp"
+#include "svnqt/revision.hpp"
+
+#include <qsqldatabase.h>
+#include <qstring.h>
+
+namespace svn
+{
+
+class Client;
+
+namespace cache
+{
+
+class SVNQT_EXPORT ReposLog
+{
+protected:
+ svn::Client*m_Client;
+ mutable QDataBase m_Database;
+ QString m_ReposRoot;
+ svn::Revision m_latestHead;
+ //! internal insert.
+ bool _insertLogEntry(const svn::LogEntry&);
+ bool checkFill(svn::Revision&_start,svn::Revision&_end,bool checkHead);
+
+public:
+ ReposLog(svn::Client*aClient,const QString&aRepository=QString::null);
+
+ QString ReposRoot() const
+ {
+ return m_ReposRoot;
+ }
+
+ QDataBase Database() const
+ {
+ return m_Database;
+ }
+ //! search for latest head revision on network for assigned repository
+ svn::Revision latestHeadRev();
+ //! return lates revision in cache
+ svn::Revision latestCachedRev();
+ //! simple retrieves logentries
+ /*!
+ * This method acts on network, too for checking if there are new entries on server.
+ *
+ * @param target where to store the result
+ * @param start revision to start for search
+ * @param end revision to end for search
+ * @param noNetwork if yes, no check on network for newer revisions will made
+ * @return true if entries found and no error, if no entries found false
+ * @exception svn::DatabaseException in case of errors
+ */
+ bool simpleLog(LogEntriesMap&target,const svn::Revision&start,const svn::Revision&end,bool noNetwork=false);
+ svn::Revision date2numberRev(const svn::Revision&,bool noNetwork=false);
+ bool fillCache(const svn::Revision&end);
+ bool insertLogEntry(const svn::LogEntry&);
+ bool log(const svn::Path&,const svn::Revision&start, const svn::Revision&end,const svn::Revision&peg,svn::LogEntriesMap&target, bool strictNodeHistory,int limit);
+ bool itemExists(const svn::Revision&,const svn::Path&);
+
+ bool isValid()const;
+};
+
+}
+}
+
+#endif
diff --git a/src/svnqt/cache/sqlite3/README b/src/svnqt/cache/sqlite3/README
new file mode 100644
index 0000000..e2f7914
--- /dev/null
+++ b/src/svnqt/cache/sqlite3/README
@@ -0,0 +1,32 @@
+With this driver you can access the files created by sqlite3 through
+the standard Qt sql module. The driver name is QSQLITE3.
+
+Although there are many other solutions to access such DB files, I think
+that using this driver has some advantages:
+
+--> You use the standard Qt interface so you can reuse exinting code or
+ switch to or from other DB types quite easily.
+
+--> Soft transition to Qt 4: Qt 4 supports sqlite3, you can prepare your
+ application now.
+
+--> The source of this driver is smaller than any other, you can incorporate
+ it on your application with little overhead and without requiring external
+ libraries.
+
+
+Developer note:
+
+The driver is a merge between the QSQLITE driver in Qt 3 and in Qt 4 beta 1, with
+small tweaks, so I think is quite stable and usable.
+Please report success or failure, thanks
+
+To compile
+
+qmake
+make
+cp sqldrivers/libqsqlite3.so $QTDIR/plugins/sqldrivers (probably as root)
+
+use it as any other Qt sql driver.
+
+Have fun, Stefano !!! \ No newline at end of file
diff --git a/src/svnqt/cache/sqlite3/qsql_sqlite3.cpp b/src/svnqt/cache/sqlite3/qsql_sqlite3.cpp
new file mode 100644
index 0000000..93010c1
--- /dev/null
+++ b/src/svnqt/cache/sqlite3/qsql_sqlite3.cpp
@@ -0,0 +1,485 @@
+/****************************************************************************
+**
+** Implementation of SQLite driver classes.
+**
+** Copyright (C) 1992-2003 Trolltech AS. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+** EDITIONS: FREE, ENTERPRISE
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+****************************************************************************/
+
+#include "qsql_sqlite3.h"
+
+#include <qdatetime.h>
+#include <qvaluevector.h>
+#include <qregexp.h>
+#include <qfile.h>
+#include <sqlite3.h>
+
+#if (QT_VERSION-0 < 0x030200)
+# include <qvector.h>
+# if !defined Q_WS_WIN32
+# include <unistd.h>
+# endif
+#else
+# include <qptrvector.h>
+# if !defined Q_WS_WIN32
+# include <unistd.h>
+# endif
+#endif
+
+typedef struct sqlite3_stmt sqlite3_stmt;
+
+#define QSQLITE3_DRIVER_NAME "QSQLITE3"
+
+static QVariant::Type qSqliteType(int tp)
+{
+ switch (tp) {
+ case SQLITE_INTEGER:
+ return QVariant::Int;
+ case SQLITE_FLOAT:
+ return QVariant::Double;
+ case SQLITE_BLOB:
+ return QVariant::ByteArray;
+ case SQLITE_TEXT:
+ default:
+ return QVariant::String;
+ }
+}
+
+static QSqlError qMakeError(sqlite3 *access, const QString &descr, QSqlError::Type type,
+ int errorCode = -1)
+{
+ return QSqlError(descr,
+ QString::fromUtf8(sqlite3_errmsg(access)),
+ type, errorCode);
+}
+
+class QSQLite3DriverPrivate
+{
+public:
+ QSQLite3DriverPrivate();
+ sqlite3 *access;
+ bool utf8;
+};
+
+QSQLite3DriverPrivate::QSQLite3DriverPrivate() : access(0)
+{
+ utf8 = true;
+}
+
+class QSQLite3ResultPrivate
+{
+public:
+ QSQLite3ResultPrivate(QSQLite3Result *res);
+ void cleanup();
+ bool fetchNext(QSqlCachedResult::ValueCache &values, int idx, bool initialFetch);
+ bool isSelect();
+ // initializes the recordInfo and the cache
+ void initColumns();
+ void finalize();
+
+ QSQLite3Result* q;
+ sqlite3 *access;
+
+ sqlite3_stmt *stmt;
+
+ uint skippedStatus: 1; // the status of the fetchNext() that's skipped
+ uint skipRow: 1; // skip the next fetchNext()?
+ uint utf8: 1;
+ QSqlRecord rInf;
+};
+
+static const uint initial_cache_size = 128;
+
+QSQLite3ResultPrivate::QSQLite3ResultPrivate(QSQLite3Result* res) : q(res), access(0),
+ stmt(0), skippedStatus(false), skipRow(false), utf8(false)
+{
+}
+
+void QSQLite3ResultPrivate::cleanup()
+{
+ finalize();
+ rInf.clear();
+ skippedStatus = false;
+ skipRow = false;
+ q->setAt(QSql::BeforeFirst);
+ q->setActive(false);
+ q->cleanup();
+}
+
+void QSQLite3ResultPrivate::finalize()
+{
+ if (!stmt)
+ return;
+
+ sqlite3_finalize(stmt);
+ stmt = 0;
+}
+
+// called on first fetch
+void QSQLite3ResultPrivate::initColumns()
+{
+ rInf.clear();
+
+ int nCols = sqlite3_column_count(stmt);
+ if (nCols <= 0)
+ return;
+
+ q->init(nCols);
+
+ for (int i = 0; i < nCols; ++i) {
+ QString colName = QString::fromUtf8(sqlite3_column_name(stmt, i));
+
+ int dotIdx = colName.findRev('.');
+ rInf.append(QSqlField(colName.mid(dotIdx == -1 ? 0 : dotIdx + 1),
+ qSqliteType(sqlite3_column_type(stmt, i))));
+ }
+}
+
+bool QSQLite3ResultPrivate::fetchNext(QSqlCachedResult::ValueCache &values, int idx, bool initialFetch)
+{
+ int res;
+ unsigned int i;
+
+ if (skipRow) {
+ // already fetched
+ Q_ASSERT(!initialFetch);
+ skipRow = false;
+ return skippedStatus;
+ }
+ skipRow = initialFetch;
+
+ if (!stmt)
+ return false;
+
+ // keep trying while busy, wish I could implement this better.
+ while ((res = sqlite3_step(stmt)) == SQLITE_BUSY) {
+ // sleep instead requesting result again immidiately.
+#if defined Q_OS_WIN
+ Sleep(1000);
+#else
+ sleep(1);
+#endif
+ }
+
+ switch(res) {
+ case SQLITE_ROW:
+ // check to see if should fill out columns
+ if (rInf.isEmpty())
+ // must be first call.
+ initColumns();
+ if (idx < 0 && !initialFetch)
+ return true;
+ for (i = 0; i < rInf.count(); ++i)
+ // todo - handle other types
+ values[i + idx] = QString::fromUtf8((char *)(sqlite3_column_text(stmt, i)));
+ // values[i + idx] = utf8 ? QString::fromUtf8(fvals[i]) : QString::fromAscii(fvals[i]);
+ return true;
+ case SQLITE_DONE:
+ if (rInf.isEmpty())
+ // must be first call.
+ initColumns();
+ q->setAt(QSql::AfterLast);
+ return false;
+ case SQLITE_ERROR:
+ case SQLITE_MISUSE:
+ default:
+ // something wrong, don't get col info, but still return false
+ q->setLastError(qMakeError(access, "Unable to fetch row", QSqlError::Connection, res));
+ finalize();
+ q->setAt(QSql::AfterLast);
+ return false;
+ }
+ return false;
+}
+
+QSQLite3Result::QSQLite3Result(const QSQLite3Driver* db)
+ : QSqlCachedResult(db)
+{
+ d = new QSQLite3ResultPrivate(this);
+ d->access = db->d->access;
+}
+
+QSQLite3Result::~QSQLite3Result()
+{
+ d->cleanup();
+ delete d;
+}
+
+/*
+ Execute \a query.
+*/
+bool QSQLite3Result::reset (const QString &query)
+{
+ // this is where we build a query.
+ if (!driver() || !driver()->isOpen() || driver()->isOpenError())
+ return false;
+
+ d->cleanup();
+
+ setSelect(false);
+
+ int res = sqlite3_prepare(d->access, query.utf8().data(), (query.length() + 1) * sizeof(QChar),
+ &d->stmt, 0);
+
+ if (res != SQLITE_OK) {
+ setLastError(qMakeError(d->access, "Unable to execute statement", QSqlError::Statement, res));
+ d->finalize();
+ return false;
+ }
+
+ d->skippedStatus = d->fetchNext(cache(), 0, true);
+
+ setSelect(!d->rInf.isEmpty());
+ setActive(true);
+ return true;
+}
+
+bool QSQLite3Result::gotoNext(QSqlCachedResult::ValueCache& row, int idx)
+{
+ return d->fetchNext(row, idx, false);
+}
+
+int QSQLite3Result::size()
+{
+ return -1;
+}
+
+int QSQLite3Result::numRowsAffected()
+{
+ return sqlite3_changes(d->access);
+}
+
+/////////////////////////////////////////////////////////
+
+QSQLite3Driver::QSQLite3Driver(QObject * parent, const char *name)
+ : QSqlDriver(parent, name)
+{
+ d = new QSQLite3DriverPrivate();
+}
+
+QSQLite3Driver::QSQLite3Driver(sqlite3 *connection, QObject *parent, const char *name)
+ : QSqlDriver(parent, name)
+{
+ d = new QSQLite3DriverPrivate();
+ d->access = connection;
+ setOpen(true);
+ setOpenError(false);
+}
+
+
+QSQLite3Driver::~QSQLite3Driver()
+{
+ delete d;
+}
+
+bool QSQLite3Driver::hasFeature(DriverFeature f) const
+{
+ switch (f) {
+ case Transactions:
+ case Unicode:
+ case BLOB:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+/*
+ SQLite dbs have no user name, passwords, hosts or ports.
+ just file names.
+*/
+bool QSQLite3Driver::open(const QString & db, const QString &, const QString &, const QString &, int, const QString &)
+{
+ if (isOpen())
+ close();
+
+ if (db.isEmpty())
+ return false;
+
+ if (sqlite3_open(QFile::encodeName(db), &d->access) == SQLITE_OK) {
+ setOpen(true);
+ setOpenError(false);
+ return true;
+ } else {
+ setLastError(qMakeError(d->access, "Error opening database",
+ QSqlError::Connection));
+ setOpenError(true);
+ return false;
+ }
+}
+
+void QSQLite3Driver::close()
+{
+ if (isOpen()) {
+ if (sqlite3_close(d->access) != SQLITE_OK)
+ setLastError(qMakeError(d->access, "Error closing database",
+ QSqlError::Connection));
+ d->access = 0;
+ setOpen(false);
+ setOpenError(false);
+ }
+}
+
+QSqlQuery QSQLite3Driver::createQuery() const
+{
+ return QSqlQuery(new QSQLite3Result(this));
+}
+
+bool QSQLite3Driver::beginTransaction()
+{
+ if (!isOpen() || isOpenError())
+ return false;
+
+ QSqlQuery q(createQuery());
+ if (!q.exec("BEGIN")) {
+ setLastError(QSqlError("Unable to begin transaction",
+ q.lastError().databaseText(), QSqlError::Transaction));
+ return false;
+ }
+
+ return true;
+}
+
+bool QSQLite3Driver::commitTransaction()
+{
+ if (!isOpen() || isOpenError())
+ return false;
+
+ QSqlQuery q(createQuery());
+ if (!q.exec("COMMIT")) {
+ setLastError(QSqlError("Unable to begin transaction",
+ q.lastError().databaseText(), QSqlError::Transaction));
+ return false;
+ }
+
+ return true;
+}
+
+bool QSQLite3Driver::rollbackTransaction()
+{
+ if (!isOpen() || isOpenError())
+ return false;
+
+ QSqlQuery q(createQuery());
+ if (!q.exec("ROLLBACK")) {
+ setLastError(QSqlError("Unable to begin transaction",
+ q.lastError().databaseText(), QSqlError::Transaction));
+ return false;
+ }
+
+ return true;
+}
+
+QStringList QSQLite3Driver::tables(const QString &typeName) const
+{
+ QStringList res;
+ if (!isOpen())
+ return res;
+ int type = typeName.toInt();
+
+ QSqlQuery q = createQuery();
+ q.setForwardOnly(TRUE);
+#if (QT_VERSION-0 >= 0x030200)
+ if ((type & (int)QSql::Tables) && (type & (int)QSql::Views))
+ q.exec("SELECT name FROM sqlite_master WHERE type='table' OR type='view'");
+ else if (typeName.isEmpty() || (type & (int)QSql::Tables))
+ q.exec("SELECT name FROM sqlite_master WHERE type='table'");
+ else if (type & (int)QSql::Views)
+ q.exec("SELECT name FROM sqlite_master WHERE type='view'");
+#else
+ q.exec("SELECT name FROM sqlite_master WHERE type='table' OR type='view'");
+#endif
+
+
+ if (q.isActive()) {
+ while(q.next())
+ res.append(q.value(0).toString());
+ }
+
+#if (QT_VERSION-0 >= 0x030200)
+ if (type & (int)QSql::SystemTables) {
+ // there are no internal tables beside this one:
+ res.append("sqlite_master");
+ }
+#endif
+
+ return res;
+}
+
+QSqlIndex QSQLite3Driver::primaryIndex(const QString &tblname) const
+{
+ QSqlRecordInfo rec(recordInfo(tblname)); // expensive :(
+
+ if (!isOpen())
+ return QSqlIndex();
+
+ QSqlQuery q = createQuery();
+ q.setForwardOnly(TRUE);
+ // finrst find a UNIQUE INDEX
+ q.exec("PRAGMA index_list('" + tblname + "');");
+ QString indexname;
+ while(q.next()) {
+ if (q.value(2).toInt()==1) {
+ indexname = q.value(1).toString();
+ break;
+ }
+ }
+ if (indexname.isEmpty())
+ return QSqlIndex();
+
+ q.exec("PRAGMA index_info('" + indexname + "');");
+
+ QSqlIndex index(indexname);
+ while(q.next()) {
+ QString name = q.value(2).toString();
+ QSqlVariant::Type type = QSqlVariant::Invalid;
+ if (rec.contains(name))
+ type = rec.find(name).type();
+ index.append(QSqlField(name, type));
+ }
+ return index;
+}
+
+QSqlRecordInfo QSQLite3Driver::recordInfo(const QString &tbl) const
+{
+ if (!isOpen())
+ return QSqlRecordInfo();
+
+ QSqlQuery q = createQuery();
+ q.setForwardOnly(TRUE);
+ q.exec("SELECT * FROM " + tbl + " LIMIT 1");
+ return recordInfo(q);
+}
+
+QSqlRecord QSQLite3Driver::record(const QString &tblname) const
+{
+ if (!isOpen())
+ return QSqlRecord();
+
+ return recordInfo(tblname).toRecord();
+}
+
+QSqlRecord QSQLite3Driver::record(const QSqlQuery& query) const
+{
+ if (query.isActive() && query.driver() == this) {
+ QSQLite3Result* result = (QSQLite3Result*)query.result();
+ return result->d->rInf;
+ }
+ return QSqlRecord();
+}
+
+QSqlRecordInfo QSQLite3Driver::recordInfo(const QSqlQuery& query) const
+{
+ if (query.isActive() && query.driver() == this) {
+ QSQLite3Result* result = (QSQLite3Result*)query.result();
+ return QSqlRecordInfo(result->d->rInf);
+ }
+ return QSqlRecordInfo();
+}
diff --git a/src/svnqt/cache/sqlite3/qsql_sqlite3.h b/src/svnqt/cache/sqlite3/qsql_sqlite3.h
new file mode 100644
index 0000000..f89c038
--- /dev/null
+++ b/src/svnqt/cache/sqlite3/qsql_sqlite3.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Definition of SQLite driver classes.
+**
+** Copyright (C) 1992-2003 Trolltech AS. All rights reserved.
+**
+** This file is part of the sql module of the Qt GUI Toolkit.
+** EDITIONS: FREE, ENTERPRISE
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+****************************************************************************/
+
+#ifndef QSQL_SQLITE3_H
+#define QSQL_SQLITE3_H
+
+#include <qsqldriver.h>
+#include <qsqlresult.h>
+#include <qsqlrecord.h>
+#include <qsqlindex.h>
+#include "qsqlcachedresult.h"
+
+#if (QT_VERSION-0 >= 0x030200)
+typedef QVariant QSqlVariant;
+#endif
+
+#if defined (Q_OS_WIN32)
+# include <qt_windows.h>
+#endif
+
+class QSQLite3DriverPrivate;
+class QSQLite3ResultPrivate;
+class QSQLite3Driver;
+struct sqlite3;
+
+class QSQLite3Result : public QSqlCachedResult
+{
+ friend class QSQLite3Driver;
+ friend class QSQLite3ResultPrivate;
+public:
+ QSQLite3Result(const QSQLite3Driver* db);
+ ~QSQLite3Result();
+
+protected:
+ bool gotoNext(QSqlCachedResult::ValueCache& row, int idx);
+ bool reset (const QString& query);
+ int size();
+ int numRowsAffected();
+
+private:
+ QSQLite3ResultPrivate* d;
+};
+
+class QSQLite3Driver : public QSqlDriver
+{
+ friend class QSQLite3Result;
+public:
+ QSQLite3Driver(QObject *parent = 0, const char *name = 0);
+ QSQLite3Driver(sqlite3 *connection, QObject *parent = 0, const char *name = 0);
+ ~QSQLite3Driver();
+ bool hasFeature(DriverFeature f) const;
+ bool open(const QString & db,
+ const QString & user,
+ const QString & password,
+ const QString & host,
+ int port,
+ const QString & connOpts);
+ bool open( const QString & db,
+ const QString & user,
+ const QString & password,
+ const QString & host,
+ int port ) { return open (db, user, password, host, port, QString()); }
+ void close();
+ QSqlQuery createQuery() const;
+ bool beginTransaction();
+ bool commitTransaction();
+ bool rollbackTransaction();
+ QStringList tables(const QString &user) const;
+
+ QSqlRecord record(const QString& tablename) const;
+ QSqlRecordInfo recordInfo(const QString& tablename) const;
+ QSqlIndex primaryIndex(const QString &table) const;
+ QSqlRecord record(const QSqlQuery& query) const;
+ QSqlRecordInfo recordInfo(const QSqlQuery& query) const;
+
+private:
+ QSQLite3DriverPrivate* d;
+};
+#endif
diff --git a/src/svnqt/cache/sqlite3/qsqlcachedresult.cpp b/src/svnqt/cache/sqlite3/qsqlcachedresult.cpp
new file mode 100644
index 0000000..8a23183
--- /dev/null
+++ b/src/svnqt/cache/sqlite3/qsqlcachedresult.cpp
@@ -0,0 +1,260 @@
+/****************************************************************************
+**
+** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
+**
+** This file is part of the sql module of the Qt Toolkit.
+**
+** This file may be distributed under the terms of the Q Public License
+** as defined by Trolltech AS of Norway and appearing in the file
+** LICENSE.QPL included in the packaging of this file.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
+** information about Qt Commercial License Agreements.
+** See http://www.trolltech.com/qpl/ for QPL licensing information.
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+****************************************************************************/
+
+#include "qsqlcachedresult.h"
+
+#include <qvariant.h>
+#include <qdatetime.h>
+#include <qvaluevector.h>
+
+static const uint initial_cache_size = 128;
+
+class QSqlCachedResultPrivate
+{
+public:
+ QSqlCachedResultPrivate();
+ bool canSeek(int i) const;
+ inline int cacheCount() const;
+ void init(int count, bool fo);
+ void cleanup();
+ int nextIndex();
+ void revertLast();
+
+ QSqlCachedResult::ValueCache cache;
+ int rowCacheEnd;
+ int colCount;
+ bool forwardOnly;
+};
+
+QSqlCachedResultPrivate::QSqlCachedResultPrivate():
+ rowCacheEnd(0), colCount(0), forwardOnly(false)
+{
+}
+
+void QSqlCachedResultPrivate::cleanup()
+{
+ cache.clear();
+ forwardOnly = false;
+ colCount = 0;
+ rowCacheEnd = 0;
+}
+
+void QSqlCachedResultPrivate::init(int count, bool fo)
+{
+ Q_ASSERT(count);
+ cleanup();
+ forwardOnly = fo;
+ colCount = count;
+ if (fo) {
+ cache.resize(count);
+ rowCacheEnd = count;
+ } else {
+ cache.resize(initial_cache_size * count);
+ }
+}
+
+int QSqlCachedResultPrivate::nextIndex()
+{
+ if (forwardOnly)
+ return 0;
+ int newIdx = rowCacheEnd;
+ if (rowCacheEnd == (int)cache.size())
+ cache.resize(cache.size() * 2);
+/* if (newIdx + colCount > cache.size()){
+ if(cache.size() * 2 < cache.size() + 10000)
+ cache.resize(cache.size() * 2);
+ else
+ cache.resize(cache.size() + 10000);
+ }*/
+ rowCacheEnd += colCount;
+
+ return newIdx;
+}
+
+bool QSqlCachedResultPrivate::canSeek(int i) const
+{
+ if (forwardOnly || i < 0)
+ return false;
+ return rowCacheEnd >= (i + 1) * colCount;
+}
+
+void QSqlCachedResultPrivate::revertLast()
+{
+ if (forwardOnly)
+ return;
+ rowCacheEnd -= colCount;
+}
+
+inline int QSqlCachedResultPrivate::cacheCount() const
+{
+ Q_ASSERT(!forwardOnly);
+ Q_ASSERT(colCount);
+ return rowCacheEnd / colCount;
+}
+
+//////////////
+
+QSqlCachedResult::QSqlCachedResult(const QSqlDriver * db): QSqlResult (db)
+{
+ d = new QSqlCachedResultPrivate();
+}
+
+QSqlCachedResult::~QSqlCachedResult()
+{
+ delete d;
+}
+
+void QSqlCachedResult::init(int colCount)
+{
+ d->init(colCount, isForwardOnly());
+}
+
+bool QSqlCachedResult::fetch(int i)
+{
+ if ((!isActive()) || (i < 0))
+ return false;
+ if (at() == i)
+ return true;
+ if (d->forwardOnly) {
+ // speed hack - do not copy values if not needed
+ if (at() > i || at() == QSql::AfterLast)
+ return false;
+ while(at() < i - 1) {
+ if (!gotoNext(d->cache, -1))
+ return false;
+ setAt(at() + 1);
+ }
+ if (!gotoNext(d->cache, 0))
+ return false;
+ setAt(at() + 1);
+ return true;
+ }
+ if (d->canSeek(i)) {
+ setAt(i);
+ return true;
+ }
+ if (d->rowCacheEnd > 0)
+ setAt(d->cacheCount()-1);
+ while (at() < i) {
+ if (!cacheNext())
+ return false;
+ }
+ return true;
+}
+
+bool QSqlCachedResult::fetchNext()
+{
+ if (d->canSeek(at() + 1)) {
+ setAt(at() + 1);
+ return true;
+ }
+ return cacheNext();
+}
+
+bool QSqlCachedResult::fetchPrevious()
+{
+ return fetch(at() - 1);
+}
+
+bool QSqlCachedResult::fetchFirst()
+{
+ if (d->forwardOnly && at() != QSql::BeforeFirst) {
+ return false;
+ }
+ if (d->canSeek(0)) {
+ setAt(0);
+ return true;
+ }
+ return cacheNext();
+}
+
+bool QSqlCachedResult::fetchLast()
+{
+ if (at() == QSql::AfterLast) {
+ if (d->forwardOnly)
+ return false;
+ else
+ return fetch(d->cacheCount() - 1);
+ }
+
+ int i = at();
+ while (fetchNext())
+ ++i; /* brute force */
+ if (d->forwardOnly && at() == QSql::AfterLast) {
+ setAt(i);
+ return true;
+ } else {
+ return fetch(i);
+ }
+}
+
+QVariant QSqlCachedResult::data(int i)
+{
+ int idx = d->forwardOnly ? i : at() * d->colCount + i;
+ if (i >= d->colCount || i < 0 || at() < 0 || idx >= d->rowCacheEnd)
+ return QVariant();
+
+ return d->cache.at(idx);
+}
+
+bool QSqlCachedResult::isNull(int i)
+{
+ int idx = d->forwardOnly ? i : at() * d->colCount + i;
+ if (i > d->colCount || i < 0 || at() < 0 || idx >= d->rowCacheEnd)
+ return true;
+
+ return d->cache.at(idx).isNull();
+}
+
+void QSqlCachedResult::cleanup()
+{
+ setAt(QSql::BeforeFirst);
+ setActive(false);
+ d->cleanup();
+}
+
+bool QSqlCachedResult::cacheNext()
+{
+ if (!gotoNext(d->cache, d->nextIndex())) {
+ d->revertLast();
+ return false;
+ }
+ setAt(at() + 1);
+ return true;
+}
+
+int QSqlCachedResult::colCount() const
+{
+ return d->colCount;
+}
+
+QSqlCachedResult::ValueCache &QSqlCachedResult::cache()
+{
+ return d->cache;
+}
+
diff --git a/src/svnqt/cache/sqlite3/qsqlcachedresult.h b/src/svnqt/cache/sqlite3/qsqlcachedresult.h
new file mode 100644
index 0000000..fa8924f
--- /dev/null
+++ b/src/svnqt/cache/sqlite3/qsqlcachedresult.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
+**
+** This file is part of the sql module of the Qt Toolkit.
+**
+** This file may be distributed under the terms of the Q Public License
+** as defined by Trolltech AS of Norway and appearing in the file
+** LICENSE.QPL included in the packaging of this file.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
+** information about Qt Commercial License Agreements.
+** See http://www.trolltech.com/qpl/ for QPL licensing information.
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+****************************************************************************/
+
+#ifndef QSQLCACHEDRESULT_P_H
+#define QSQLCACHEDRESULT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qsqlresult.h>
+
+class QVariant;
+template <typename T> class QValueVector;
+
+class QSqlCachedResultPrivate;
+
+class QM_EXPORT_SQL QSqlCachedResult: public QSqlResult
+{
+public:
+ virtual ~QSqlCachedResult();
+
+ typedef QValueVector<QVariant> ValueCache;
+
+protected:
+ QSqlCachedResult(const QSqlDriver * db);
+
+ void init(int colCount);
+ void cleanup();
+
+ virtual bool gotoNext(ValueCache &values, int index) = 0;
+
+ QVariant data(int i);
+ bool isNull(int i);
+ bool fetch(int i);
+ bool fetchNext();
+ bool fetchPrevious();
+ bool fetchFirst();
+ bool fetchLast();
+
+ int colCount() const;
+ ValueCache &cache();
+
+private:
+ bool cacheNext();
+ QSqlCachedResultPrivate *d;
+};
+
+#endif // QSQLCACHEDRESULT_P_H
diff --git a/src/svnqt/cache/test/CMakeLists.txt b/src/svnqt/cache/test/CMakeLists.txt
new file mode 100644
index 0000000..ecc6130
--- /dev/null
+++ b/src/svnqt/cache/test/CMakeLists.txt
@@ -0,0 +1,19 @@
+SET(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR})
+
+MACRO(BUILD_TEST tname)
+ SET(${tname}-src ${tname}.cpp)
+ IF (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${tname}.h)
+ SET(${tname}-src ${${tname}-src} ${tname}.h)
+ ENDIF (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${tname}.h)
+ ADD_EXECUTABLE(${tname} ${${tname}-src})
+ TARGET_LINK_LIBRARIES(${tname} ${svnqt-name} ${QT_LIBRARIES})
+ ADD_TEST(${tname} ${CMAKE_CURRENT_BINARY_DIR}/${tname})
+ENDMACRO(BUILD_TEST)
+
+IF (BUILD_TESTS)
+ CONFIGURE_FILE(
+ ${CMAKE_CURRENT_SOURCE_DIR}/testconfig.h.in
+ ${CMAKE_CURRENT_BINARY_DIR}/testconfig.h
+ )
+ BUILD_TEST(sqlite)
+ENDIF(BUILD_TESTS)
diff --git a/src/svnqt/cache/test/sqlite.cpp b/src/svnqt/cache/test/sqlite.cpp
new file mode 100644
index 0000000..4f14b2d
--- /dev/null
+++ b/src/svnqt/cache/test/sqlite.cpp
@@ -0,0 +1,111 @@
+#include <qsql.h>
+#include <qsqldatabase.h>
+#include <qstringlist.h>
+#include <iostream>
+#include <qapplication.h>
+#include <qtextstream.h>
+
+#include "svnqt/client.hpp"
+#include "svnqt/svnqttypes.hpp"
+#include "svnqt/log_entry.hpp"
+
+#include "svnqt/cache/LogCache.hpp"
+#include "svnqt/cache/ReposLog.hpp"
+#include "svnqt/cache/test/testconfig.h"
+#include "svnqt/cache/DatabaseException.hpp"
+
+#if QT_VERSION < 0x040000
+#else
+#include <QSqlQuery>
+#include <QSqlError>
+#endif
+
+int main(int argc,char**argv)
+{
+ QApplication app(argc,argv);
+
+ svn::ContextP m_CurrentContext;
+ svn::Client* m_Svnclient;
+ m_Svnclient=svn::Client::getobject(0,0);
+ m_CurrentContext = new svn::Context();
+
+ m_Svnclient->setContext(m_CurrentContext);
+
+ QStringList list;
+ QStringList::Iterator it;
+ // goes into "self" of logcache
+ new svn::cache::LogCache(TESTDBPATH);
+ list = QSqlDatabase::drivers();
+ it = list.begin();
+ while( it != list.end() ) {
+ std::cout << (*it).TOUTF8().data() << std::endl;
+ ++it;
+ }
+ svn::cache::ReposLog rl(m_Svnclient,"http://www.alwins-world.de/repos/kdesvn");
+ QDataBase db = rl.Database();
+#if QT_VERSION < 0x040000
+ if (!db) {
+#else
+ if (!db.isValid()) {
+#endif
+ std::cerr << "No database object."<<std::endl;
+ exit(-1);
+ }
+#if QT_VERSION < 0x040000
+ list = db->tables();
+#else
+ list = db.tables();
+#endif
+ it = list.begin();
+ while( it != list.end() ) {
+ std::cout << ( *it ).TOUTF8().data() << std::endl;
+ ++it;
+ }
+ svn::LogEntriesMap lm;
+ try {
+ rl.simpleLog(lm,100,svn::Revision::HEAD);
+ }
+ catch (const svn::cache::DatabaseException&cl)
+ {
+ std::cerr << cl.msg().TOUTF8().data() <<std::endl;
+ }
+ catch (const svn::Exception&ce)
+ {
+ std::cerr << "Exception: " << ce.msg().TOUTF8().data() <<std::endl;
+ }
+ svn::LogEntriesMap::ConstIterator lit = lm.begin();
+ std::cout<<"Count: "<<lm.count()<<std::endl;
+
+ svn::Revision r("{2006-09-27}");
+ std::cout << r.toString().TOUTF8().data() << " -> " << rl.date2numberRev(r).toString().TOUTF8().data()<<std::endl;
+ r = svn::Revision::HEAD;
+ std::cout << rl.date2numberRev(r).toString().TOUTF8().data()<<std::endl;
+ try {
+ rl.insertLogEntry(lm[100]);
+ }
+ catch (const svn::cache::DatabaseException&cl)
+ {
+ std::cerr << cl.msg().TOUTF8().data() << std::endl;
+ }
+ QSqlQuery q("insert into logentries(revision,date,author,message) values ('100','1122591406','alwin','copy and moving works now in basic form')",db);
+ q.exec();
+ std::cerr << "\n" << q.lastError().text().TOUTF8().data()<<std::endl;
+
+#if QT_VERSION < 0x040000
+#else
+ db=QSqlDatabase();
+#endif
+ try {
+ rl.log("/trunk/src/svnqt",1,1000,svn::Revision::UNDEFINED,lm,false,-1);
+ }
+ catch (const svn::cache::DatabaseException&cl)
+ {
+ std::cerr << cl.msg().TOUTF8().data() <<std::endl;
+ }
+ catch (const svn::Exception&ce)
+ {
+ std::cerr << "Exception: " << ce.msg().TOUTF8().data() <<std::endl;
+ }
+ std::cout<<"Count: "<<lm.count()<<std::endl;
+ return 0;
+}
diff --git a/src/svnqt/cache/test/testconfig.h.in b/src/svnqt/cache/test/testconfig.h.in
new file mode 100644
index 0000000..865ac6e
--- /dev/null
+++ b/src/svnqt/cache/test/testconfig.h.in
@@ -0,0 +1,8 @@
+#ifndef __TEST_CONFIG_H
+#define __TEST_CONFIG_H
+
+#define TESTREPOPATH "@CMAKE_CURRENT_BINARY_DIR@/repo"
+#define TESTCOPATH "@CMAKE_CURRENT_BINARY_DIR@/co"
+#define TESTDBPATH "@CMAKE_CURRENT_BINARY_DIR@/db"
+
+#endif