From: <mrj...@us...> - 2008-05-18 22:03:17
|
Revision: 2756 http://tora.svn.sourceforge.net/tora/?rev=2756&view=rev Author: mrjohnson0 Date: 2008-05-18 15:03:08 -0700 (Sun, 18 May 2008) Log Message: ----------- A connection pool for tora. Some fixes for toeventquery Modified Paths: -------------- trunk/tora/src/Makefile.am trunk/tora/src/toalert.cpp trunk/tora/src/toalert.h trunk/tora/src/toconnection.cpp trunk/tora/src/toconnection.h trunk/tora/src/toeventquery.cpp trunk/tora/src/toeventquery.h trunk/tora/src/toeventquerytask.cpp trunk/tora/src/toeventquerytask.h trunk/tora/src/toglobalsetting.cpp trunk/tora/src/tomain.cpp trunk/tora/src/tomain.h trunk/tora/src/toresultmodel.cpp Added Paths: ----------- trunk/tora/src/toconnectionpool.cpp trunk/tora/src/toconnectionpool.h Modified: trunk/tora/src/Makefile.am =================================================================== --- trunk/tora/src/Makefile.am 2008-05-18 19:22:17 UTC (rev 2755) +++ trunk/tora/src/Makefile.am 2008-05-18 22:03:08 UTC (rev 2756) @@ -59,6 +59,7 @@ tobackgroundlabel.cpp tobackgroundlabel.h \ toconfiguration.cpp toconfiguration.h \ toconnection.cpp toconnection.h \ + toconnectionpool.cpp toconnectionpool.h \ toeditwidget.cpp toeditwidget.h \ toglobalsetting.cpp toglobalsetting.h \ tohelp.cpp tohelp.h tohelpsetup.h \ @@ -268,6 +269,7 @@ moc_tochangeconnection.cpp \ moc_tochartmanager.cpp \ moc_toconnection.cpp \ + moc_toconnectionpool.cpp \ moc_tonoblockquery.cpp \ moc_toeventquery.cpp \ moc_toeventquerytask.cpp \ Modified: trunk/tora/src/toalert.cpp =================================================================== --- trunk/tora/src/toalert.cpp 2008-05-18 19:22:17 UTC (rev 2755) +++ trunk/tora/src/toalert.cpp 2008-05-18 22:03:08 UTC (rev 2756) @@ -42,6 +42,7 @@ #include "tomain.h" #include "tomemoeditor.h" #include "toresultview.h" +#include "toconnection.h" #ifndef Q_OS_WIN32 #include <unistd.h> Modified: trunk/tora/src/toalert.h =================================================================== --- trunk/tora/src/toalert.h 2008-05-18 19:22:17 UTC (rev 2755) +++ trunk/tora/src/toalert.h 2008-05-18 22:03:08 UTC (rev 2756) @@ -45,12 +45,13 @@ #include <qtimer.h> #include <QMenu> -#include "toconnection.h" #include "totool.h" +#include "tothread.h" class QComboBox; class QLineEdit; class toListView; +class toConnection; class toAlert : public toToolWidget { Modified: trunk/tora/src/toconnection.cpp =================================================================== --- trunk/tora/src/toconnection.cpp 2008-05-18 19:22:17 UTC (rev 2755) +++ trunk/tora/src/toconnection.cpp 2008-05-18 22:03:08 UTC (rev 2756) @@ -43,6 +43,7 @@ #include "tomain.h" #include "tosql.h" #include "totool.h" +#include "toconnectionpool.h" #include <string> #include <time.h> @@ -785,8 +786,8 @@ const QString &arg8, const QString &arg9) : Connection(QPointer<toConnection>(&conn)), - ConnectionSub(conn.mainConnection()), - SQL(sql(*Connection).toAscii()) + ConnectionSub(conn.pooledConnection()), + SQL(sql(*Connection).toAscii()) { Mode = Normal; int numArgs; @@ -841,7 +842,7 @@ { delete Query; Query = NULL; - Connection->freeConnection(ConnectionSub); + Connection->ConnectionPool->release(ConnectionSub); throw; } ConnectionSub->setQuery(this); @@ -859,7 +860,7 @@ const QString &arg8, const QString &arg9) : Connection(QPointer<toConnection>(&conn)), - ConnectionSub(conn.mainConnection()), + ConnectionSub(conn.pooledConnection()), SQL(sql.toUtf8()) { Mode = Normal; @@ -915,7 +916,7 @@ { delete Query; Query = NULL; - Connection->freeConnection(ConnectionSub); + Connection->ConnectionPool->release(ConnectionSub); throw; } ConnectionSub->setQuery(this); @@ -923,7 +924,7 @@ toQuery::toQuery(toConnection &conn, const toSQL &sql, const toQList ¶ms) : Connection(QPointer<toConnection>(&conn)), - ConnectionSub(conn.mainConnection()), + ConnectionSub(conn.pooledConnection()), Params(params), SQL(sql(conn).toAscii()) { @@ -939,15 +940,42 @@ { delete Query; Query = NULL; - Connection->freeConnection(ConnectionSub); + Connection->ConnectionPool->release(ConnectionSub); throw; } ConnectionSub->setQuery(this); } +// for testing sub +toQuery::toQuery(toConnection &conn, + toConnectionSub *sub, + const QString &sql, + const toQList ¶ms) + : Connection(QPointer<toConnection>(&conn)), + ConnectionSub(sub), + Params(params), + SQL(sql) +{ + Mode = Test; + toBusy busy; + try + { + Query = NULL; + Query = conn.Connection->createQuery(this, ConnectionSub); + Query->execute(); + } + catch (...) + { + delete Query; + Query = NULL; + throw; + } + ConnectionSub->setQuery(this); +} + toQuery::toQuery(toConnection &conn, const QString &sql, const toQList ¶ms) : Connection(QPointer<toConnection>(&conn)), - ConnectionSub(conn.mainConnection()), + ConnectionSub(conn.pooledConnection()), Params(params), SQL(sql.toUtf8()) { @@ -963,7 +991,7 @@ { delete Query; Query = NULL; - Connection->freeConnection(ConnectionSub); + Connection->ConnectionPool->release(ConnectionSub); throw; } ConnectionSub->setQuery(this); @@ -979,19 +1007,7 @@ { Mode = mode; - switch (Mode) - { - case Normal: - case All: - ConnectionSub = conn.mainConnection(); - break; - case Background: - ConnectionSub = conn.backgroundConnection(); - break; - case Long: - ConnectionSub = conn.longConnection(); - break; - } + ConnectionSub = conn.pooledConnection(); toBusy busy; try @@ -1004,7 +1020,7 @@ { delete Query; Query = NULL; - Connection->freeConnection(ConnectionSub); + Connection->ConnectionPool->release(ConnectionSub); throw; } ConnectionSub->setQuery(this); @@ -1020,19 +1036,7 @@ { Mode = mode; - switch (Mode) - { - case Normal: - case All: - ConnectionSub = conn.mainConnection(); - break; - case Background: - ConnectionSub = conn.backgroundConnection(); - break; - case Long: - ConnectionSub = conn.longConnection(); - break; - } + ConnectionSub = conn.pooledConnection(); toBusy busy; try @@ -1045,7 +1049,7 @@ { delete Query; Query = NULL; - Connection->freeConnection(ConnectionSub); + Connection->ConnectionPool->release(ConnectionSub); throw; } ConnectionSub->setQuery(this); @@ -1056,19 +1060,7 @@ { Mode = mode; - switch (Mode) - { - case Normal: - case All: - ConnectionSub = conn.mainConnection(); - break; - case Background: - ConnectionSub = conn.backgroundConnection(); - break; - case Long: - ConnectionSub = conn.longConnection(); - break; - } + ConnectionSub = conn.pooledConnection(); toBusy busy; try @@ -1109,47 +1101,14 @@ { if (ConnectionSub->query() == this) ConnectionSub->setQuery(NULL); - if (Connection) - Connection->freeConnection(ConnectionSub); + if (Mode != Test && Connection && Connection->ConnectionPool) + Connection->ConnectionPool->release(ConnectionSub); } catch (...) {} } bool toQuery::eof(void) { - if (Mode == All) - { - if (Query->eof()) - { - Connection->Lock.lock(); - bool found = false; - try - { - std::list<toConnectionSub *> &cons = Connection->connections(); - for (std::list<toConnectionSub *>::iterator i = cons.begin();i != cons.end();i++) - { - if (*i == ConnectionSub) - { - i++; - if (i != cons.end()) - { - ConnectionSub = *i; - Connection->Lock.unlock(); - found = true; - delete Query; - Query = NULL; - Query = connection().Connection->createQuery(this, ConnectionSub); - Query->execute(); - Connection->Lock.lock(); - } - break; - } - } - Connection->Lock.unlock(); - } - catch (...) {} - } - } return Query->eof(); } @@ -1261,8 +1220,6 @@ toBusy busy; if (Connection->Abort) throw qApp->translate("toQuery", "Query aborted"); - if (Mode == All) - eof(); return toNull(Query->readValue()); } @@ -1274,8 +1231,6 @@ toBusy busy; if (Connection->Abort) throw qApp->translate("toQuery", "Query aborted"); - if (Mode == All) - eof(); return Query->readValue(); } @@ -1286,13 +1241,12 @@ // toConnection implementation -void toConnection::addConnection(void) +toConnectionSub* toConnection::addConnection() { toBusy busy; toConnectionSub *sub = Connection->createConnection(); toLocker lock(Lock) ; - Connections.insert(Connections.end(), sub); toQList params; for (std::list<QString>::iterator i = InitStrings.begin();i != InitStrings.end();i++) { @@ -1302,21 +1256,31 @@ } TOCATCH } + + return sub; } + +void toConnection::closeConnection(toConnectionSub *sub) { + if(Connection) + Connection->closeConnection(sub); +} + + toConnection::toConnection(const QString &provider, const QString &user, const QString &password, const QString &host, const QString &database, const std::set<QString> &options, bool cache) : Provider(provider), User(user), Password(password), Host(host), Database(database), Options(options) { - BackgroundConnection = NULL; - BackgroundCount = 0; Connection = toConnectionProvider::connection(Provider, this); - addConnection(); - Version = Connection->version(mainConnection()); NeedCommit = Abort = false; ReadingCache = false; + ConnectionPool = new toConnectionPool(this); + + PoolPtr sub(ConnectionPool); + Version = Connection->version(*sub); + if (cache) { if (toConfigurationSingle::Instance().objectCache() == 1) @@ -1337,15 +1301,15 @@ Database(conn.Database), Options(conn.Options) { - BackgroundConnection = NULL; - BackgroundCount = 0; Connection = toConnectionProvider::connection(Provider, this); - addConnection(); - Version = Connection->version(mainConnection()); ReadingValues.up(); ReadingValues.up(); ReadingCache = false; NeedCommit = Abort = false; + ConnectionPool = new toConnectionPool(this); + + PoolPtr sub(ConnectionPool); + Version = Connection->version(*sub); } std::list<QString> toConnection::running(void) @@ -1354,187 +1318,69 @@ toLocker lock(Lock) ; std::list<QString> ret; - toConnectionSub *sub = (*(Connections.begin())); - if (sub && sub->query()) - ret.insert(ret.end(), sub->query()->sql()); - if (BackgroundConnection && BackgroundConnection->query()) - ret.insert(ret.end(), BackgroundConnection->query()->sql()); - for (std::list<toConnectionSub *>::const_iterator i = Running.begin();i != Running.end();i++) - { - sub = *i; - if (sub && sub->query()) - ret.insert(ret.end(), sub->query()->sql()); - } + // this is insane. disabled code that tried to get sql from + // running queries return ret; } void toConnection::cancelAll(void) { - toBusy busy; - toLocker lock(Lock) - ; - for (std::list<toConnectionSub *>::iterator i = Running.begin();i != Running.end();i++) - (*i)->cancel(); + ConnectionPool->cancelAll(); } toConnection::~toConnection() { toBusy busy; + Abort = true; { + cancelAll(); + toLocker lock(Lock); - // widgets is a std::list of qpointers. we don't own those - // pointers, so don't delete them. they are qobjects and will - // be deleted with their parents. - closeWidgets(); + delete ConnectionPool; + ConnectionPool = 0; - for (std::list<toConnectionSub *>::iterator i = Running.begin();i != Running.end();i++) - try - { - (*i)->cancel(); - } - catch (...) {} + closeWidgets(); } - Abort = true; if (ReadingCache) { ReadingValues.down(); ReadingValues.down(); } - for (std::list<toConnectionSub *>::iterator i = Connections.begin();i != Connections.end();i++) - { - try - { - Connection->closeConnection(*i); - } - catch (...) {} - } delete Connection; } -toConnectionSub *toConnection::mainConnection() +toConnectionSub* toConnection::pooledConnection(void) { - if (Connection->handleMultipleQueries()) - { - toLocker lock(Lock) - ; - return (*(Connections.begin())); - } - else - { - return longConnection(); - } + return ConnectionPool->borrow(); } -toConnectionSub *toConnection::backgroundConnection() +void toConnection::commit(toConnectionSub *sub) { - if (!Connection->handleMultipleQueries()) - return longConnection(); - if (toConfigurationSingle::Instance().bkgndConnect()) - return mainConnection(); - Lock.lock(); - if (!BackgroundConnection) - { - Lock.unlock(); - toConnectionSub *tmp = longConnection(); - Lock.lock(); + toBusy busy; + toLocker lock(Lock); - BackgroundConnection = tmp; - BackgroundCount = 0; - } - BackgroundCount++; - Lock.unlock(); - return BackgroundConnection; + Connection->commit(sub); } -toConnectionSub *toConnection::longConnection() +void toConnection::commit(void) { - Lock.lock(); - bool multiple = Connection->handleMultipleQueries(); - if ((multiple && Connections.size() == 1) || - (!multiple && Connections.empty())) - { - Lock.unlock(); - addConnection(); - } - else - Lock.unlock(); - toLocker lock(Lock) - ; - std::list<toConnectionSub *>::iterator i = Connections.begin(); - if (multiple) - i++; - toConnectionSub *ret = (*i); - Connections.erase(i); - Running.insert(Running.end(), ret); - return ret; + ConnectionPool->commit(); + setNeedCommit(false); } -void toConnection::freeConnection(toConnectionSub *sub) +void toConnection::rollback(toConnectionSub *sub) { - toLocker lock(Lock) - ; - if (sub == BackgroundConnection) - { - BackgroundCount--; - if (BackgroundCount > 0) - return ; - BackgroundConnection = NULL; - } - { - for (std::list<toConnectionSub *>::iterator i = Running.begin();i != Running.end();i++) - { - if (*i == sub) - { - Running.erase(i); - break; - } - } - } - { - for (std::list<toConnectionSub *>::iterator i = Connections.begin();i != Connections.end();i++) - { - if (*i == sub) - return ; - } - } - Connections.insert(Connections.end(), sub); -} - -void toConnection::commit(void) -{ toBusy busy; - toLocker lock(Lock) - ; - for (std::list<toConnectionSub *>::iterator i = Connections.begin();i != Connections.end();i++) - Connection->commit(*i); - while (Connections.size() > 2) - { - std::list<toConnectionSub *>::iterator i = Connections.begin(); - i++; - delete(*i); - Connections.erase(i); - } - setNeedCommit(false); + toLocker lock(Lock); + + Connection->rollback(sub); } void toConnection::rollback(void) { - toBusy busy; - toLocker lock(Lock) - ; - for (std::list<toConnectionSub *>::iterator i = Connections.begin();i != Connections.end();i++) - { - Connection->rollback(*i); - } - while (Connections.size() > 2) - { - std::list<toConnectionSub *>::iterator i = Connections.begin(); - i++; - delete(*i); - Connections.erase(i); - } + ConnectionPool->rollback(); setNeedCommit(false); } @@ -1628,25 +1474,41 @@ void toConnection::parse(const QString &sql) { toBusy busy; - Connection->parse(mainConnection(), sql.toUtf8()); + + PoolPtr sub(ConnectionPool); + Version = Connection->version(*sub); + + Connection->parse(*sub, sql.toUtf8()); } void toConnection::parse(const toSQL &sql) { toBusy busy; - Connection->parse(mainConnection(), toSQL::sql(sql, *this)); + + PoolPtr sub(ConnectionPool); + Version = Connection->version(*sub); + + Connection->parse(*sub, toSQL::sql(sql, *this)); } void toConnection::execute(const toSQL &sql, toQList ¶ms) { toBusy busy; - Connection->execute(mainConnection(), toSQL::sql(sql, *this), params); + + PoolPtr sub(ConnectionPool); + Version = Connection->version(*sub); + + Connection->execute(*sub, toSQL::sql(sql, *this), params); } void toConnection::execute(const QString &sql, toQList ¶ms) { toBusy busy; - Connection->execute(mainConnection(), sql.toUtf8(), params); + + PoolPtr sub(ConnectionPool); + Version = Connection->version(*sub); + + Connection->execute(*sub, sql.toUtf8(), params); } void toConnection::execute(const toSQL &sql, @@ -1699,7 +1561,11 @@ args.insert(args.end(), arg9); toBusy busy; - Connection->execute(mainConnection(), toSQL::sql(sql, *this), args); + + PoolPtr sub(ConnectionPool); + Version = Connection->version(*sub); + + Connection->execute(*sub, toSQL::sql(sql, *this), args); } void toConnection::execute(const QString &sql, @@ -1752,38 +1618,21 @@ args.insert(args.end(), arg9); toBusy busy; - Connection->execute(mainConnection(), sql.toUtf8(), args); + + PoolPtr sub(ConnectionPool); + Version = Connection->version(*sub); + + Connection->execute(*sub, sql.toUtf8(), args); } void toConnection::allExecute(const toSQL &sql, toQList ¶ms) { - toBusy busy; - toLocker lock(Lock) - ; - for (std::list<toConnectionSub *>::iterator i = Connections.begin();i != Connections.end();i++) - { - try - { - if(*i) - Connection->execute(*i, toSQL::sql(sql, *this), params); - } - TOCATCH - } + ConnectionPool->executeAll(toSQL::sql(sql, *this), params); } void toConnection::allExecute(const QString &sql, toQList ¶ms) { - toBusy busy; - toLocker lock(Lock) - ; - for (std::list<toConnectionSub *>::iterator i = Connections.begin();i != Connections.end();i++) - { - try - { - Connection->execute(*i, sql.toUtf8(), params); - } - TOCATCH - } + ConnectionPool->executeAll(sql, params); } void toConnection::allExecute(const toSQL &sql, @@ -1835,17 +1684,7 @@ if (numArgs > 8) args.insert(args.end(), arg9); - toBusy busy; - toLocker lock(Lock) - ; - for (std::list<toConnectionSub *>::iterator i = Connections.begin();i != Connections.end();i++) - { - try - { - Connection->execute(*i, toSQL::sql(sql, *this), args); - } - TOCATCH - } + ConnectionPool->executeAll(toSQL::sql(sql, *this), args); } void toConnection::allExecute(const QString &sql, @@ -1897,17 +1736,7 @@ if (numArgs > 8) args.insert(args.end(), arg9); - toBusy busy; - toLocker lock(Lock) - ; - for (std::list<toConnectionSub *>::iterator i = Connections.begin();i != Connections.end();i++) - { - try - { - Connection->execute(*i, sql.toUtf8(), args); - } - TOCATCH - } + ConnectionPool->executeAll(sql, args); } const QString &toConnection::provider(void) const @@ -2062,25 +1891,38 @@ bool diskloaded = false; try { - diskloaded = Connection.loadDiskCache(); + diskloaded = Connection->loadDiskCache(); if (!diskloaded) { - Connection.ObjectNames = Connection.Connection->objectNames(); + std::list<objectName> objs; + objs = Connection->Connection->objectNames(); + if(Connection && !Connection->Abort) { + toLocker lock(Connection->Lock); + Connection->ObjectNames = objs; + } + else + return; } - Connection.ObjectNames.sort(); - Connection.ReadingValues.up(); + Connection->ObjectNames.sort(); + Connection->ReadingValues.up(); if (!diskloaded) { - Connection.SynonymMap = Connection.Connection->synonymMap(Connection.ObjectNames); - Connection.writeDiskCache(); + std::map<QString, objectName> syn; + syn = Connection->Connection->synonymMap(Connection->ObjectNames); + if(Connection && !Connection->Abort) { + toLocker lock(Connection->Lock); + Connection->SynonymMap = syn; + Connection->writeDiskCache(); + } } } catch (...) { - if (Connection.ReadingValues.getValue() == 0) - Connection.ReadingValues.up(); + if (Connection && Connection->ReadingValues.getValue() == 0) + Connection->ReadingValues.up(); } - Connection.ReadingValues.up(); + if(Connection) + Connection->ReadingValues.up(); } @@ -2097,7 +1939,7 @@ ReadingCache = true; try { - (new toThread(new cacheObjects(*this)))->start(); + (new toThread(new cacheObjects(this)))->start(); } catch (...) { Modified: trunk/tora/src/toconnection.h =================================================================== --- trunk/tora/src/toconnection.h 2008-05-18 19:22:17 UTC (rev 2755) +++ trunk/tora/src/toconnection.h 2008-05-18 22:03:08 UTC (rev 2756) @@ -57,6 +57,7 @@ class toSQL; class toQuery; class toSyntaxAnalyzer; +class toConnectionPool; /** This class is an abstract definition of an actual connection to a database. * Each @ref toConnection object can have one or more actual connections to the @@ -123,7 +124,10 @@ Long, /** Run the query on all non occupied connections of the @ref toConnection object. */ - All + All, + /** For internal use, doesn't close resources + */ + Test }; /** This structure is used to describe the resultset of a query. @@ -274,6 +278,19 @@ const toSQL &sql, const std::list<toQValue> ¶ms); + /** Internal use. Create a query object to test a + * toConnectionSub. Does not close sub. + * + * @param conn Connection to create query on. + * @param mode Mode to run query in. + * @param sql SQL to run. + * @param params Arguments to pass to query. + */ + toQuery(toConnection &conn, + toConnectionSub *sub, + const QString &sql, + const std::list<toQValue> ¶ms); + /** Create a query. * @param conn Connection to create query on. * @param mode Mode to run query in. @@ -485,22 +502,18 @@ QString Version; std::list<QPointer<QWidget> > Widgets; std::list<QString> InitStrings; - std::set - <QString> Options; + std::set<QString> Options; toLock Lock; - std::list<toConnectionSub *> Connections; - std::list<toConnectionSub *> Running; - int BackgroundCount; - toConnectionSub *BackgroundConnection; bool NeedCommit; + toConnectionPool *ConnectionPool; + public: /** Class that could be used to throw exceptions in connection errors. Must use if you * want to indicate error offset. */ - -class exception : public QString + class exception : public QString { int Offset; public: @@ -682,19 +695,16 @@ private: - void addConnection(void); - std::list<toConnectionSub *> &connections(void) - { - return Connections; - } + toConnectionSub* addConnection(void); + void closeConnection(toConnectionSub *sub); connectionImpl *Connection; class cacheObjects : public toTask { - toConnection &Connection; + QPointer<toConnection> Connection; public: - cacheObjects(toConnection &conn) + cacheObjects(toConnection *conn) : Connection(conn) { } virtual void run(void); @@ -708,10 +718,7 @@ std::list<objectName> ObjectNames; std::map<QString, objectName> SynonymMap; - toConnectionSub *mainConnection(void); - toConnectionSub *longConnection(void); - toConnectionSub *backgroundConnection(void); - void freeConnection(toConnectionSub *); + toConnectionSub* pooledConnection(void); void readObjects(void); QString cacheFile(); @@ -817,12 +824,23 @@ { return NeedCommit; } + /** - * Commit connection. This will also close all extra connections except one. + * Commit connection implementation */ + void commit(toConnectionSub *sub); + + /** + * Rollback connection implementation + */ + void rollback(toConnectionSub *rollback); + + /** + * Commit all connections. + */ virtual void commit(void); /** - * Rollback connection. This will also close all extra connections except one. + * Rollback all connections. */ virtual void rollback(void); @@ -1027,6 +1045,7 @@ static QString cacheDir(); friend class toQuery; + friend class toConnectionPool; }; Q_DECLARE_METATYPE(toConnection::exception); Added: trunk/tora/src/toconnectionpool.cpp =================================================================== --- trunk/tora/src/toconnectionpool.cpp (rev 0) +++ trunk/tora/src/toconnectionpool.cpp 2008-05-18 22:03:08 UTC (rev 2756) @@ -0,0 +1,341 @@ +/***** +* +* TOra - An Oracle Toolkit for DBA's and developers +* Copyright (C) 2003-2005 Quest Software, Inc +* Portions Copyright (C) 2005 Other Contributors +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; only version 2 of +* the License is valid for this program. +* +* 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 General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +* As a special exception, you have permission to link this program +* with the Oracle Client libraries and distribute executables, as long +* as you follow the requirements of the GNU GPL in regard to all of the +* software in the executable aside from Oracle client libraries. +* +* Specifically you are not permitted to link this program with the +* Qt/UNIX, Qt/Windows or Qt Non Commercial products of TrollTech. +* And you are not permitted to distribute binaries compiled against +* these libraries without written consent from Quest Software, Inc. +* Observe that this does not disallow linking to the Qt Free Edition. +* +* You may link this product with any GPL'd Qt library such as Qt/Free +* +* All trademarks belong to their respective owners. +* +*****/ + +#include "utils.h" + +#include "toconf.h" +#include "toconnectionpool.h" +#include "tosql.h" + +#include <QMutexLocker> +#include <QTimer> +#include <QCoreApplication> + + +static const int TEST_MSEC = 5000; + + +toConnectionPoolTest::toConnectionPoolTest(toConnectionPool *pool) + : QThread(0) { + Pool = pool; +} + + +void toConnectionPoolTest::run() { + QTimer::singleShot(TEST_MSEC, this, SLOT(test())); + exec(); +} + + +void toConnectionPoolTest::test() { + for(int i = 0; Pool && i < Pool->Pool.size(); i++) { + toConnectionPool::PooledState state = Pool->test(i); + if(state == toConnectionPool::Busy) + continue; + + if(state == toConnectionPool::Broken) + Pool->fix(i); + } + + QTimer::singleShot(TEST_MSEC, this, SLOT(test())); +} + + +toConnectionPoolExec::toConnectionPoolExec(toConnectionPool *pool, Action act) { + action = act; + Pool = pool; +} + + +toConnectionPoolExec::toConnectionPoolExec(toConnectionPool *pool, + Action act, + const QString &sql, + toQList ¶ms) { + action = act; + Pool = pool; + Sql = sql; + Params = params; +} + + +void toConnectionPoolExec::run() { + for(int mem = 0; mem < Pool->Pool.size(); mem++) { + toConnectionSub *sub = Pool->steal(mem); + if(!sub) + continue; + + try { + switch(action) { + case Commit: + Pool->Connection->commit(sub); + break; + + case Rollback: + Pool->Connection->rollback(sub); + break; + + case Cancel: + sub->cancel(); + // send it back to the pool + Pool->release(sub); + break; + + case Execute: { + toQuery q(*(Pool->Connection), sub, Sql, Params); + break; + } + + } // switch + } + TOCATCH; // show errors to user + } + + // thread deletes self + QCoreApplication::postEvent(this, new ExecFinished(this)); +} + + +void toConnectionPoolExec::customEvent(QEvent *event) { + ExecFinished *e = dynamic_cast<ExecFinished *>(event); + if(e) { + QThread *t = e->thread(); + if(t) { + t->exit(); + t->wait(); + delete t; + } + } +} + + +toConnectionPool::toConnectionPool(toConnection *conn) : QObject(conn) { + Connection = conn; + QMutexLocker lock(&PoolLock); + + for(int i = 0; i < PreferredSize; i++) { + PooledSub *psub = new PooledSub; + try { + psub->Sub = Connection->addConnection(); + psub->State = Free; + } + catch(...) { + psub->State = Broken; + } + + Pool.append(psub); + } + + TestThread = new toConnectionPoolTest(this); + TestThread->start(); +} + + +toConnectionPool::~toConnectionPool() { + TestThread->exit(); + TestThread->wait(); + delete TestThread; + + toConnection *conn = Connection; + Connection = 0; + + QMutexLocker lock(&PoolLock); + + for(int mem = 0; mem < Pool.size(); mem++) { + PooledSub *psub = Pool[mem]; + try { + psub->Sub->cancel(); + } + catch(...) { + } + + conn->closeConnection(psub->Sub); + } + + while(!Pool.isEmpty()) + delete Pool.takeFirst(); +} + + +void toConnectionPool::fix(int member) { + if(!Connection) + return; + + QMutexLocker lock(&PoolLock); + PooledSub *psub = Pool[member]; + psub->State = Broken; + lock.unlock(); + + Connection->closeConnection(psub->Sub); + try { + psub->Sub = Connection->addConnection(); + psub->State = Free; + } + catch(...) { + psub->State = Broken; + } +} + + +toConnectionPool::PooledState toConnectionPool::test(int member) { + if(!Connection) + return Broken; + + QMutexLocker lock(&PoolLock); + PooledSub *psub = Pool[member]; + if(psub->State != Free) + return psub->State; + + psub->State = Busy; + lock.unlock(); + + psub->State = test(psub); + return psub->State; +} + + +toConnectionPool::PooledState toConnectionPool::test(PooledSub *sub) { + PooledState state = Free; + try { + toQList params; + toQuery q(*Connection, + sub->Sub, + toSQL::string("Global:Now", *Connection), + params); + } + catch(...) { + state = Broken; + } + + return state; +} + + +toConnectionSub* toConnectionPool::steal(int member) { + QMutexLocker lock(&PoolLock); + return Pool[member]->Sub; +} + + +toConnectionSub* toConnectionPool::borrow() { + QMutexLocker lock(&PoolLock); + + try { + for(int mem = 0; mem < Pool.size(); mem++) { + PooledSub *psub = Pool[mem]; + + if(psub->State == Free) { + psub->State = Busy; + lock.unlock(); + + PooledState state = test(psub); + if(state == Free) + return psub->Sub; + else + psub->State = state; + + lock.relock(); + + // be careful adding code after this, as the pool size + // might have changed since lock was last acquired. + } + } + } + catch(...) { + throw; + } + + PooledSub *psub = new PooledSub(Connection->addConnection(), Busy); + Pool.append(psub); + return psub->Sub; +} + + +void toConnectionPool::remove(int member) { + QMutexLocker lock(&PoolLock); + + PooledSub *psub = Pool.takeAt(member); + Connection->closeConnection(psub->Sub); + delete psub; +} + + +void toConnectionPool::release(toConnectionSub *sub) { + QMutexLocker lock(&PoolLock); + + for(int mem = 0; mem < Pool.size(); mem++) { + PooledSub *psub = Pool[mem]; + if(psub->Sub == sub) { + + // if needscommit is false, we can eliminate extra + // connections here. + if(Pool.size() > PreferredSize && + Connection && + !Connection->needCommit()) { + + lock.unlock(); + remove(mem); + } + else + psub->State = Free; + + return; + } + } + + if(Connection) + Connection->closeConnection(sub); +} + + +void toConnectionPool::commit() { + (new toConnectionPoolExec(this, toConnectionPoolExec::Commit))->start(); +} + + +void toConnectionPool::rollback() { + (new toConnectionPoolExec(this, toConnectionPoolExec::Rollback))->start(); +} + + +void toConnectionPool::cancelAll(void) { + (new toConnectionPoolExec(this, toConnectionPoolExec::Cancel))->start(); +} + + +void toConnectionPool::executeAll(const QString &sql, toQList ¶ms) { + (new toConnectionPoolExec(this, toConnectionPoolExec::Execute, sql, params))->start(); +} Added: trunk/tora/src/toconnectionpool.h =================================================================== --- trunk/tora/src/toconnectionpool.h (rev 0) +++ trunk/tora/src/toconnectionpool.h 2008-05-18 22:03:08 UTC (rev 2756) @@ -0,0 +1,272 @@ +/***** + * + * TOra - An Oracle Toolkit for DBA's and developers + * Copyright (C) 2003-2005 Quest Software, Inc + * Portions Copyright (C) 2005 Other Contributors + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; only version 2 of + * the License is valid for this program. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * As a special exception, you have permission to link this program + * with the Oracle Client libraries and distribute executables, as long + * as you follow the requirements of the GNU GPL in regard to all of the + * software in the executable aside from Oracle client libraries. + * + * Specifically you are not permitted to link this program with the + * Qt/UNIX, Qt/Windows or Qt Non Commercial products of TrollTech. + * And you are not permitted to distribute binaries compiled against + * these libraries without written consent from Quest Software, Inc. + * Observe that this does not disallow linking to the Qt Free Edition. + * + * You may link this product with any GPL'd Qt library such as Qt/Free + * + * All trademarks belong to their respective owners. + * + *****/ + +#ifndef TOCONNECTIONPOOL_H +#define TOCONNECTIONPOOL_H + +#include "toconnection.h" + +#include <QMutex> +#include <QList> +#include <QObject> +#include <QPointer> +#include <QWaitCondition> +#include <QThread> +#include <QEvent> + +class toConnectionPool; + +class ExecFinished : public QEvent { + QPointer<QThread> Thread; + +public: + ExecFinished(QThread *t) : QEvent(QEvent::User) { + Thread = t; + } + + + QThread* thread() { + return Thread; + } +}; + + +/** + * Tests connections in pool + * + */ +class toConnectionPoolTest : public QThread { + Q_OBJECT; + + QPointer<toConnectionPool> Pool; + +public: + toConnectionPoolTest(toConnectionPool *pool); + + /** + * Overrides QThread::run. Call start() to execute thread. + * + */ + virtual void run(void); + +private slots: + // execute tests + void test(void); +}; + + +/** + * Executes sql on pool connections + * + */ +class toConnectionPoolExec : public QThread { + Q_OBJECT; + +public: + enum Action { + Commit, + Rollback, + Cancel, + Execute + }; + +private: + QPointer<toConnectionPool> Pool; + Action action; + + // for Execute + toQList Params; + QString Sql; + + +public: + toConnectionPoolExec(toConnectionPool *pool, Action act); + + toConnectionPoolExec(toConnectionPool *pool, + Action act, + const QString &sql, + toQList ¶ms); + + + /** + * Overrides QThread::run. Call start() to execute thread. + * + */ + virtual void run(void); + +protected: + virtual void customEvent(QEvent *event); +}; + + +class toConnectionPool : public QObject { + Q_OBJECT; + + friend class toConnectionPoolTest; + friend class toConnectionPoolExec; + + toConnectionPoolTest *TestThread; + + // for future configuration + static const int PreferredSize = 3; + + enum PooledState { + Busy, + Free, + Broken + }; + + class PooledSub { + public: + toConnectionSub *Sub; + PooledState State; + + // need this for template container + PooledSub() { + Sub = 0; + State = Broken; + } + + PooledSub(toConnectionSub *sub, PooledState state = Free) { + Sub = sub; + State = state; + } + }; + + + // the pool + QList<PooledSub *> Pool; + + // lock for the pool. + QMutex PoolLock; + + // toConnection instance this class is a member of. will be used + // to create new connections when needed. + QPointer<toConnection> Connection; + + // this is used internally to test + PooledState test(PooledSub *sub); + PooledState test(int member); + + // get a specific connection. + toConnectionSub* steal(int member); + + // remove a connection from pool + void remove(int member); + + // create new connection at position + void fix(int member); + +public: + toConnectionPool(toConnection *conn); + ~toConnectionPool(); + + + /** + * Get a connection from pool. + * + * This method is thread safe + */ + toConnectionSub* borrow(void); + + + + /** + * Release connection back to pool + * + * This method is thread safe + */ + void release(toConnectionSub *sub); + + + /** + * Commit connections that are not busy + * + */ + void commit(void); + + + /** + * Rollback connections that are not busy + * + */ + void rollback(void); + + + /** + * Cancels all running queries + * + */ + void cancelAll(void); + + + /** + * Execute sql on all connections + * + */ + void executeAll(const QString &sql, toQList ¶ms); +}; + + +/** + * Encapsulates returning toConnectionSub to pool for easier exception + * proof code. + * + */ +class PoolPtr { + QPointer<toConnectionPool> Pool; + toConnectionSub *Sub; + +public: + PoolPtr(toConnectionPool *pool) { + Pool = pool; + Sub = Pool->borrow(); + } + + + toConnectionSub* operator*() { + return Sub; + } + + + ~PoolPtr() { + if(Pool) + Pool->release(Sub); + } +}; + +#endif Modified: trunk/tora/src/toeventquery.cpp =================================================================== --- trunk/tora/src/toeventquery.cpp 2008-05-18 19:22:17 UTC (rev 2755) +++ trunk/tora/src/toeventquery.cpp 2008-05-18 22:03:08 UTC (rev 2756) @@ -96,6 +96,7 @@ void toEventQuery::start() { Task = new toEventQueryTask(this, *Connection, SQL, Param, Statistics); + Task->ThreadAlive.lock(); qRegisterMetaType<toQDescList>("toQDescList&"); qRegisterMetaType<ValuesList>("ValuesList&"); @@ -124,6 +125,12 @@ SLOT(taskFinished()), Qt::QueuedConnection); + connect(Task, + SIGNAL(done()), + this, + SLOT(taskFinished()), + Qt::QueuedConnection); + Task->start(); } @@ -132,7 +139,8 @@ try { if(Task) { disconnect(Task, 0, 0, 0); - stop(); + Task->exit(); + Task->ThreadAlive.unlock(); } } catch(...) { Modified: trunk/tora/src/toeventquery.h =================================================================== --- trunk/tora/src/toeventquery.h 2008-05-18 19:22:17 UTC (rev 2755) +++ trunk/tora/src/toeventquery.h 2008-05-18 22:03:08 UTC (rev 2756) @@ -87,7 +87,7 @@ // finish before it's messages have all been processed, making // Task->isRunning() useless for detecting if there's data // waiting. - bool TaskDone; + volatile bool TaskDone; // connection for this query toConnection *Connection; Modified: trunk/tora/src/toeventquerytask.cpp =================================================================== --- trunk/tora/src/toeventquerytask.cpp 2008-05-18 19:22:17 UTC (rev 2755) +++ trunk/tora/src/toeventquerytask.cpp 2008-05-18 22:03:08 UTC (rev 2756) @@ -49,20 +49,20 @@ #define CATCH_ALL \ catch(const toConnection::exception &str) { \ if(!Closed) { \ + emit error(str); \ close(); \ - emit error(str); \ } \ } \ catch(const QString &str) { \ if(!Closed) { \ + emit error(str); \ close(); \ - emit error(str); \ } \ } \ catch(...) { \ if(!Closed) { \ + emit error(tr("Unknown exception")); \ close(); \ - emit error(tr("Unknown exception")); \ } \ } @@ -103,13 +103,13 @@ // emit empty result ValuesList values; emit data(values); - return; } + else { + read(); - read(); - - // begin thread's event loop - exec(); + // begin thread's event loop + exec(); + } } CATCH_ALL; @@ -131,6 +131,7 @@ // ignored } + ThreadAlive.lock(); QCoreApplication::postEvent(this, new FinishedEvent(this)); } @@ -144,6 +145,7 @@ if(e) { QThread *t = e->thread(); if(t) { + t->exit(); t->wait(); delete t; } @@ -153,7 +155,11 @@ void toEventQueryTask::close() { try { - if(Query) + if(isRunning()) + emit done(); + + disconnect(this, 0, 0, 0); + if(Query && !Closed) Query->cancel(); } catch(...) { @@ -168,11 +174,14 @@ void toEventQueryTask::read(bool all) { - emit readRequested(all); + if(isRunning()) + emit readRequested(all); } void toEventQueryTask::pread(bool all) { + if(!isRunning()) + return; if(!Query || Columns < 1) { close(); return; Modified: trunk/tora/src/toeventquerytask.h =================================================================== --- trunk/tora/src/toeventquerytask.h 2008-05-18 19:22:17 UTC (rev 2755) +++ trunk/tora/src/toeventquerytask.h 2008-05-18 22:03:08 UTC (rev 2756) @@ -46,6 +46,7 @@ #include <QPointer> #include <QMetaType> #include <QEvent> +#include <QMutex> typedef QList<toQValue> ValuesList; Q_DECLARE_METATYPE(ValuesList); @@ -93,11 +94,16 @@ toConnection *Connection; // object was closed - bool Closed; + volatile bool Closed; public: + // keeps thread from exiting until toEventQuery exits. this + // prevents many race conditions and the caller can always assume + // task is alive. + QMutex ThreadAlive; + toEventQueryTask(QObject *parent, toConnection &conn, const QString &sql, @@ -169,6 +175,13 @@ * */ void data(ValuesList &values); + + + /** + * Emitted when sql query is done + * + */ + void done(); }; #endif Modified: trunk/tora/src/toglobalsetting.cpp =================================================================== --- trunk/tora/src/toglobalsetting.cpp 2008-05-18 19:22:17 UTC (rev 2755) +++ trunk/tora/src/toglobalsetting.cpp 2008-05-18 22:03:08 UTC (rev 2756) @@ -333,7 +333,6 @@ toConfigurationSingle::Instance().setNumberDecimals(Decimals->value()); toQValue::setNumberFormat(NumberFormat->currentIndex(), Decimals->value()); - toMainWidget()->updateKeepAlive(); toUpdateIndicateEmpty(); } Modified: trunk/tora/src/tomain.cpp =================================================================== --- trunk/tora/src/tomain.cpp 2008-05-18 19:22:17 UTC (rev 2755) +++ trunk/tora/src/tomain.cpp 2008-05-18 22:03:08 UTC (rev 2756) @@ -1723,46 +1723,6 @@ dialog.exec(); } -void toMain::updateKeepAlive(void) -{ - int keepAlive = toConfigurationSingle::Instance().keepAlive(); - if (KeepAlive.isActive()) - disconnect(&KeepAlive, SIGNAL(timeout()), this, SLOT(keepAlive())); - if (keepAlive) - { - connect(&KeepAlive, SIGNAL(timeout()), this, SLOT(keepAlive())); - KeepAlive.start(keepAlive*1000); - } -} - -class toMainNopExecutor : public toTask -{ -private: - toConnection &Connection; - QString SQL; -public: - toMainNopExecutor(toConnection &conn, const QString &sql) - : Connection(conn), SQL(sql) - { } - virtual void run(void) - { - try - { - Connection.allExecute(SQL); - } - TOCATCH - } -}; - -void toMain::keepAlive(void) -{ - for (std::list<toConnection *>::iterator i = Connections.begin();i != Connections.end();i++) - { - toThread *thread = new toThread(new toMainNopExecutor(*(*i), toSQL::string("Global:Now", *(*i)))); - thread->start(); - } -} - void toMain::toolWidgetAdded(toToolWidget *tool) { emit addedToolWidget(tool); Modified: trunk/tora/src/tomain.h =================================================================== --- trunk/tora/src/tomain.h 2008-05-18 19:22:17 UTC (rev 2755) +++ trunk/tora/src/tomain.h 2008-05-18 22:03:08 UTC (rev 2756) @@ -168,8 +168,6 @@ toBackgroundLabel* BackgroundLabel; - QTimer KeepAlive; - toEditWidget *Edit; toEditWidget *findEdit(QWidget *edit); @@ -299,10 +297,6 @@ toBackgroundLabel* getBackgroundLabel(); - /* Update the keepalive from configuration - */ - void updateKeepAlive(); - /** * Get a list of currently open connections. * @return List of connection names. The returned list can then be used by @@ -543,8 +537,6 @@ */ void displayMessage(void); - void keepAlive(); - /** * Internal slot for handling status bar messages * Modified: trunk/tora/src/toresultmodel.cpp =================================================================== --- trunk/tora/src/toresultmodel.cpp 2008-05-18 19:22:17 UTC (rev 2755) +++ trunk/tora/src/toresultmodel.cpp 2008-05-18 22:03:08 UTC (rev 2756) @@ -99,10 +99,11 @@ disconnect(Query, 0, this, 0); Query->stop(); + delete Query; emit done(); } - Query = NULL; + Query = 0; } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |