00001 #include "unistd.h"
00002 #include "stdlib.h"
00003
00004 #include "mythdbcon.h"
00005
00006 #include <qsqldriver.h>
00007 #include <qregexp.h>
00008 #include <qvaluevector.h>
00009
00010 #include "compat.h"
00011
00012 MSqlDatabase::MSqlDatabase(const QString &name)
00013 {
00014 m_name = name;
00015 m_db = QSqlDatabase::addDatabase("QMYSQL3", name);
00016
00017 if (!m_db)
00018 {
00019 VERBOSE(VB_IMPORTANT,
00020 "Unable to init db connection.");
00021 return;
00022 }
00023 m_lastDBKick = QDateTime::currentDateTime().addSecs(-60);
00024 }
00025
00026 MSqlDatabase::~MSqlDatabase()
00027 {
00028 if (m_db)
00029 {
00030 m_db->close();
00031 QSqlDatabase::removeDatabase(m_name);
00032 m_db = NULL;
00033 }
00034 }
00035
00036 bool MSqlDatabase::isOpen()
00037 {
00038 if (m_db)
00039 {
00040 if (!m_db->hostName().length())
00041 return true;
00042
00043 if (m_db->isOpen())
00044 return true;
00045 }
00046 return false;
00047 }
00048
00049 bool MSqlDatabase::OpenDatabase()
00050 {
00051 if (!m_db)
00052 {
00053 VERBOSE(VB_IMPORTANT,
00054 "MSqlDatabase::OpenDatabase(), db object is NULL!");
00055 return false;
00056 }
00057
00058 bool connected = true;
00059
00060 if (!m_db->isOpen())
00061 {
00062 DatabaseParams dbparms = gContext->GetDatabaseParams();
00063 m_db->setDatabaseName(dbparms.dbName);
00064 m_db->setUserName(dbparms.dbUserName);
00065 m_db->setPassword(dbparms.dbPassword);
00066 m_db->setHostName(dbparms.dbHostName);
00067
00068 if (!dbparms.dbHostName.length())
00069 {
00070 connected = true;
00071 return true;
00072 }
00073
00074 if (dbparms.dbPort)
00075 m_db->setPort(dbparms.dbPort);
00076
00077 if (dbparms.dbPort && dbparms.dbHostName == "localhost")
00078 m_db->setHostName("127.0.0.1");
00079
00080 connected = m_db->open();
00081
00082 if (!connected && dbparms.wolEnabled)
00083 {
00084 int trycount = 0;
00085
00086 while (!connected && trycount++ < dbparms.wolRetry)
00087 {
00088 VERBOSE(VB_GENERAL, QString(
00089 "Using WOL to wakeup database server (Try %1 of %2)")
00090 .arg(trycount).arg(dbparms.wolRetry));
00091
00092 system(dbparms.wolCommand);
00093 sleep(dbparms.wolReconnect);
00094 connected = m_db->open();
00095 }
00096
00097 if (!connected)
00098 {
00099 VERBOSE(VB_IMPORTANT, "WOL failed, unable to connect to database!");
00100 }
00101 }
00102 if (connected)
00103 {
00104 VERBOSE(VB_GENERAL,
00105 QString("Connected to database '%1' at host: %2")
00106 .arg(m_db->databaseName()).arg(m_db->hostName()));
00107 }
00108 }
00109
00110 if (!connected)
00111 {
00112 VERBOSE(VB_IMPORTANT, "Unable to connect to database!");
00113 VERBOSE(VB_IMPORTANT, MythContext::DBErrorMessage(m_db->lastError()));
00114 }
00115
00116 return connected;
00117 }
00118
00119 bool MSqlDatabase::KickDatabase()
00120 {
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133 if (!m_db->hostName().length())
00134 {
00135 m_lastDBKick = QDateTime::currentDateTime();
00136 return true;
00137 }
00138
00139 if (m_lastDBKick.secsTo(QDateTime::currentDateTime()) < 30 &&
00140 m_db->isOpen())
00141 {
00142 return true;
00143 }
00144
00145 QString query("SELECT NULL;");
00146 for (unsigned int i = 0 ; i < 2 ; ++i, usleep(50000))
00147 {
00148 QSqlQuery result = m_db->exec(query);
00149 if (result.isActive())
00150 {
00151 m_lastDBKick = QDateTime::currentDateTime();
00152 return true;
00153 }
00154 else if (i == 0)
00155 {
00156 m_db->close();
00157 m_db->open();
00158 }
00159 else
00160 MythContext::DBError("KickDatabase", result);
00161 }
00162
00163 m_lastDBKick = QDateTime::currentDateTime().addSecs(-60);
00164
00165 return false;
00166 }
00167
00168
00169
00170
00171
00172
00173 MDBManager::MDBManager()
00174 {
00175 m_connID = 0;
00176 m_pool.setAutoDelete(false);
00177
00178 m_sem = new QSemaphore(20);
00179
00180 m_schedCon = NULL;
00181 m_DDCon = NULL;
00182 }
00183
00184 MDBManager::~MDBManager()
00185 {
00186 m_pool.setAutoDelete(true);
00187 delete m_sem;
00188 }
00189
00190 MSqlDatabase *MDBManager::popConnection()
00191 {
00192 (*m_sem)++;
00193 m_lock.lock();
00194
00195 MSqlDatabase *db = m_pool.getLast();
00196 m_pool.removeLast();
00197
00198 if (!db)
00199 {
00200 db = new MSqlDatabase("DBManager" + QString::number(m_connID++));
00201 VERBOSE(VB_IMPORTANT, QString("New DB connection, total: %1").arg(m_connID));
00202 }
00203
00204 m_lock.unlock();
00205
00206 db->OpenDatabase();
00207
00208 return db;
00209 }
00210
00211 void MDBManager::pushConnection(MSqlDatabase *db)
00212 {
00213 m_lock.lock();
00214
00215 if (db)
00216 {
00217 m_pool.append(db);
00218 }
00219
00220 m_lock.unlock();
00221 (*m_sem)--;
00222 }
00223
00224 MSqlDatabase *MDBManager::getSchedCon()
00225 {
00226 if (!m_schedCon)
00227 {
00228 m_schedCon = new MSqlDatabase("SchedCon");
00229 VERBOSE(VB_IMPORTANT, "New DB scheduler connection");
00230 }
00231
00232 m_schedCon->OpenDatabase();
00233
00234 return m_schedCon;
00235 }
00236
00237 MSqlDatabase *MDBManager::getDDCon()
00238 {
00239 if (!m_DDCon)
00240 {
00241 m_DDCon = new MSqlDatabase("DataDirectCon");
00242 VERBOSE(VB_IMPORTANT, "New DB DataDirect connection");
00243 }
00244
00245 m_DDCon->OpenDatabase();
00246
00247 return m_DDCon;
00248 }
00249
00250
00251
00252 void MDBManager::CloseDatabases()
00253 {
00254 m_lock.lock();
00255
00256 QPtrListIterator<MSqlDatabase> it(m_pool);
00257 MSqlDatabase *db;
00258
00259 while ((db = it.current()))
00260 {
00261 VERBOSE(VB_IMPORTANT,
00262 "Closing DB connection named '" + db->m_name + "'");
00263 db->m_db->close();
00264 ++it;
00265 }
00266
00267 m_lock.unlock();
00268 }
00269
00270
00271
00272
00273 void InitMSqlQueryInfo(MSqlQueryInfo &qi)
00274 {
00275 qi.db = NULL;
00276 qi.qsqldb = NULL;
00277 qi.returnConnection = true;
00278 }
00279
00280
00281 MSqlQuery::MSqlQuery(const MSqlQueryInfo &qi) : QSqlQuery(QString::null, qi.qsqldb)
00282 {
00283 m_isConnected = false;
00284 m_db = qi.db;
00285 m_returnConnection = qi.returnConnection;
00286
00287 m_isConnected = m_db && m_db->isOpen();
00288 }
00289
00290 MSqlQuery::~MSqlQuery()
00291 {
00292 if (!gContext)
00293 {
00294 VERBOSE(VB_IMPORTANT, "~MSqlQuery::gContext null");
00295 return;
00296 }
00297
00298 if (m_returnConnection)
00299 {
00300 MDBManager *dbmanager = gContext->GetDBManager();
00301 if (dbmanager && m_db)
00302 {
00303 dbmanager->pushConnection(m_db);
00304 }
00305 }
00306 }
00307
00308 MSqlQueryInfo MSqlQuery::InitCon()
00309 {
00310 MSqlQueryInfo qi;
00311 InitMSqlQueryInfo(qi);
00312
00313 if (gContext)
00314 {
00315 MSqlDatabase *db = gContext->GetDBManager()->popConnection();
00316 if (db)
00317 {
00318 qi.db = db;
00319 qi.qsqldb = db->db();
00320
00321 db->KickDatabase();
00322 }
00323 }
00324 else
00325 {
00326 VERBOSE(VB_IMPORTANT, "MSqlQuery::InitCon gContext null");
00327 }
00328
00329 return qi;
00330 }
00331
00332 MSqlQueryInfo MSqlQuery::SchedCon()
00333 {
00334 MSqlQueryInfo qi;
00335 InitMSqlQueryInfo(qi);
00336 qi.returnConnection = false;
00337
00338 if (gContext)
00339 {
00340 MSqlDatabase *db = gContext->GetDBManager()->getSchedCon();
00341 if (db)
00342 {
00343 qi.db = db;
00344 qi.qsqldb = db->db();
00345
00346 db->KickDatabase();
00347 }
00348 }
00349 else
00350 {
00351 VERBOSE(VB_IMPORTANT, "MSqlQuery::SchedCon gContext null");
00352 }
00353
00354 return qi;
00355 }
00356
00357 MSqlQueryInfo MSqlQuery::DDCon()
00358 {
00359 MSqlQueryInfo qi;
00360 InitMSqlQueryInfo(qi);
00361 qi.returnConnection = false;
00362
00363 if (gContext)
00364 {
00365 MSqlDatabase *db = gContext->GetDBManager()->getDDCon();
00366 if (db)
00367 {
00368 qi.db = db;
00369 qi.qsqldb = db->db();
00370
00371 db->KickDatabase();
00372 }
00373 }
00374 else
00375 {
00376 VERBOSE(VB_IMPORTANT, "MSqlQuery::DDCon gContext null");
00377 }
00378
00379 return qi;
00380 }
00381
00382
00383
00384
00385 bool MSqlQuery::exec(const QString &query)
00386 {
00387 if (!m_db->m_db->hostName().length())
00388 return true;
00389
00390 bool result = QSqlQuery::exec(query);
00391
00392 if (print_verbose_messages & VB_DATABASE)
00393 {
00394 QString str = "";
00395
00396 #if QT_VERSION >= 0x030200
00397 str += "MSqlQuery: ";
00398 str += executedQuery();
00399 #else
00400 str += "Your Qt version is too old to provide proper SQL debugging\n";
00401 str += "MSqlQuery: ";
00402 str += lastQuery();
00403 #endif
00404
00405 VERBOSE(VB_DATABASE, str);
00406 }
00407
00408 return result;
00409 }
00410
00411 bool MSqlQuery::prepare(const QString& query)
00412 {
00413 if (!m_db->m_db->hostName().length())
00414 return true;
00415
00416 static QMutex prepareLock;
00417 QMutexLocker lock(&prepareLock);
00418 return QSqlQuery::prepare(query);
00419 }
00420
00421 bool MSqlQuery::testDBConnection()
00422 {
00423 MSqlQuery query(MSqlQuery::InitCon());
00424 return query.isConnected();
00425 }
00426
00427 void MSqlQuery::bindValues(MSqlBindings &bindings)
00428 {
00429 MSqlBindings::Iterator it;
00430 for (it = bindings.begin(); it != bindings.end(); ++it)
00431 {
00432 bindValue(it.key(), it.data());
00433 }
00434 }
00435
00436 QVariant MSqlQuery::lastInsertId()
00437 {
00438 if (!m_db->m_db->hostName().length())
00439 return value(0);
00440
00441 exec("SELECT LAST_INSERT_ID();");
00442 if (!isActive() || size() < 1)
00443 {
00444 MythContext::DBError("selecting last insert id", *this);
00445 return QVariant();
00446 }
00447
00448 next();
00449 return value(0);
00450 }
00451
00452 void MSqlAddMoreBindings(MSqlBindings &output, MSqlBindings &addfrom)
00453 {
00454 MSqlBindings::Iterator it;
00455 for (it = addfrom.begin(); it != addfrom.end(); ++it)
00456 {
00457 output.insert(it.key(), it.data());
00458 }
00459 }
00460
00461 struct Holder {
00462 Holder( const QString& hldr = QString::null, int pos = -1 ): holderName( hldr ), holderPos( pos ) {}
00463 bool operator==( const Holder& h ) const { return h.holderPos == holderPos && h.holderName == holderName; }
00464 bool operator!=( const Holder& h ) const { return h.holderPos != holderPos || h.holderName != holderName; }
00465 QString holderName;
00466 int holderPos;
00467 };
00468
00469 void MSqlEscapeAsAQuery(QString &query, MSqlBindings &bindings)
00470 {
00471 MSqlQuery result(MSqlQuery::InitCon());
00472
00473 QString q = query;
00474 QRegExp rx(QString::fromLatin1("'[^']*'|:([a-zA-Z0-9_]+)"));
00475
00476 QValueVector<Holder> holders;
00477
00478 int i = 0;
00479 while ((i = rx.search(q, i)) != -1)
00480 {
00481 if (!rx.cap(1).isEmpty())
00482 holders.append(Holder(rx.cap(0), i));
00483 i += rx.matchedLength();
00484 }
00485
00486 q = query;
00487 QVariant val;
00488 QString holder;
00489
00490 for (i = (int)holders.count() - 1; i >= 0; --i)
00491 {
00492 holder = holders[(uint)i].holderName;
00493 val = bindings[holder];
00494 QSqlField f("", val.type());
00495 if (val.isNull())
00496 f.setNull();
00497 else
00498 f.setValue(val);
00499
00500 query = query.replace((uint)holders[(uint)i].holderPos, holder.length(),
00501 result.driver()->formatValue(&f));
00502 }
00503 }
00504