00001 #include <unistd.h>
00002
00003
00004 #include <cstdlib>
00005
00006
00007 #include <QVector>
00008 #include <QSqlDriver>
00009 #include <QSemaphore>
00010 #include <QSqlError>
00011 #include <QSqlField>
00012 #include <QSqlRecord>
00013
00014
00015 #include "compat.h"
00016 #include "mythdbcon.h"
00017 #include "mythdb.h"
00018 #include "mythcorecontext.h"
00019 #include "mythlogging.h"
00020 #include "mythsystem.h"
00021 #include "exitcodes.h"
00022 #include "mthread.h"
00023
00024 #define DEBUG_RECONNECT 0
00025 #if DEBUG_RECONNECT
00026 #include <stdlib.h>
00027 #endif
00028
00029 static const uint kPurgeTimeout = 60 * 60;
00030
00031 bool TestDatabase(QString dbHostName,
00032 QString dbUserName,
00033 QString dbPassword,
00034 QString dbName,
00035 int dbPort)
00036 {
00037 bool ret = false;
00038
00039 if (dbHostName.isEmpty() || dbUserName.isEmpty())
00040 return ret;
00041
00042 MSqlDatabase *db = new MSqlDatabase("dbtest");
00043
00044 if (!db)
00045 return ret;
00046
00047 DatabaseParams dbparms;
00048 dbparms.dbName = dbName;
00049 dbparms.dbUserName = dbUserName;
00050 dbparms.dbPassword = dbPassword;
00051 dbparms.dbHostName = dbHostName;
00052 dbparms.dbPort = dbPort;
00053
00054
00055 dbparms.wolEnabled = false;
00056 dbparms.wolReconnect = 1;
00057 dbparms.wolRetry = 3;
00058 dbparms.wolCommand = QString();
00059
00060 db->SetDBParams(dbparms);
00061
00062 ret = db->OpenDatabase(true);
00063
00064 delete db;
00065 db = NULL;
00066
00067 return ret;
00068 }
00069
00070 MSqlDatabase::MSqlDatabase(const QString &name)
00071 {
00072 m_name = name;
00073 m_name.detach();
00074 m_db = QSqlDatabase::addDatabase("QMYSQL", m_name);
00075 LOG(VB_DATABASE, LOG_INFO, "Database connection created: " + m_name);
00076
00077 if (!m_db.isValid())
00078 {
00079 LOG(VB_GENERAL, LOG_ERR, "Unable to init db connection.");
00080 return;
00081 }
00082 m_lastDBKick = QDateTime::currentDateTime().addSecs(-60);
00083 }
00084
00085 MSqlDatabase::~MSqlDatabase()
00086 {
00087 if (m_db.isOpen())
00088 {
00089 m_db.close();
00090 m_db = QSqlDatabase();
00091
00092
00093 QSqlDatabase::removeDatabase(m_name);
00094 LOG(VB_DATABASE, LOG_INFO, "Database connection deleted: " + m_name);
00095 }
00096 }
00097
00098 bool MSqlDatabase::isOpen()
00099 {
00100 if (m_db.isValid())
00101 {
00102 if (m_db.isOpen())
00103 return true;
00104 }
00105 return false;
00106 }
00107
00108 bool MSqlDatabase::OpenDatabase(bool skipdb)
00109 {
00110 if (!m_db.isValid())
00111 {
00112 LOG(VB_GENERAL, LOG_ERR,
00113 "MSqlDatabase::OpenDatabase(), db object is not valid!");
00114 return false;
00115 }
00116
00117 bool connected = true;
00118
00119 if (!m_db.isOpen())
00120 {
00121 if (!skipdb)
00122 m_dbparms = GetMythDB()->GetDatabaseParams();
00123 m_db.setDatabaseName(m_dbparms.dbName);
00124 m_db.setUserName(m_dbparms.dbUserName);
00125 m_db.setPassword(m_dbparms.dbPassword);
00126 m_db.setHostName(m_dbparms.dbHostName);
00127
00128 if (m_dbparms.dbHostName.isEmpty())
00129 {
00130 connected = true;
00131 return true;
00132 }
00133
00134 if (m_dbparms.dbPort)
00135 m_db.setPort(m_dbparms.dbPort);
00136
00137
00138
00139
00140
00141 if ((m_dbparms.dbPort == 0 || m_dbparms.dbPort == 3306) &&
00142 m_dbparms.dbHostName == "127.0.0.1")
00143 m_db.setHostName("localhost");
00144
00145 connected = m_db.open();
00146
00147 if (!connected && m_dbparms.wolEnabled)
00148 {
00149 int trycount = 0;
00150
00151 while (!connected && trycount++ < m_dbparms.wolRetry)
00152 {
00153 LOG(VB_GENERAL, LOG_INFO,
00154 QString("Using WOL to wakeup database server (Try %1 of "
00155 "%2)")
00156 .arg(trycount).arg(m_dbparms.wolRetry));
00157
00158 if (myth_system(m_dbparms.wolCommand) != GENERIC_EXIT_OK)
00159 {
00160 LOG(VB_GENERAL, LOG_ERR,
00161 QString("Failed to run WOL command '%1'")
00162 .arg(m_dbparms.wolCommand));
00163 }
00164
00165 sleep(m_dbparms.wolReconnect);
00166 connected = m_db.open();
00167 }
00168
00169 if (!connected)
00170 {
00171 LOG(VB_GENERAL, LOG_ERR,
00172 "WOL failed, unable to connect to database!");
00173 }
00174 }
00175 if (connected)
00176 {
00177 LOG(VB_DATABASE, LOG_INFO,
00178 QString("Connected to database '%1' at host: %2")
00179 .arg(m_db.databaseName()).arg(m_db.hostName()));
00180
00181
00182
00183 GetMythDB()->SetHaveDBConnection(true);
00184 if (!GetMythDB()->HaveSchema())
00185 {
00186
00187
00188
00189 bool have_schema = false;
00190 QString sql = "SELECT COUNT( "
00191 " INFORMATION_SCHEMA.TABLES.TABLE_NAME "
00192 " ) "
00193 " FROM INFORMATION_SCHEMA.TABLES "
00194 " WHERE INFORMATION_SCHEMA.TABLES.TABLE_SCHEMA "
00195 " = DATABASE() "
00196 " AND INFORMATION_SCHEMA.TABLES.TABLE_TYPE = "
00197 " 'BASE TABLE';";
00198
00199
00200
00201 QSqlQuery query = m_db.exec(sql);
00202 if (query.next())
00203 have_schema = query.value(0).toInt() > 1;
00204 GetMythDB()->SetHaveSchema(have_schema);
00205 }
00206 GetMythDB()->WriteDelayedSettings();
00207 }
00208 }
00209
00210 if (!connected)
00211 {
00212 GetMythDB()->SetHaveDBConnection(false);
00213 LOG(VB_GENERAL, LOG_ERR, "Unable to connect to database!");
00214 LOG(VB_GENERAL, LOG_ERR, MythDB::DBErrorMessage(m_db.lastError()));
00215 }
00216
00217 return connected;
00218 }
00219
00220 bool MSqlDatabase::KickDatabase(void)
00221 {
00222 m_lastDBKick = QDateTime::currentDateTime().addSecs(-60);
00223
00224 if (!m_db.isOpen())
00225 m_db.open();
00226
00227 return m_db.isOpen();
00228 }
00229
00230 bool MSqlDatabase::Reconnect()
00231 {
00232 m_db.close();
00233 m_db.open();
00234
00235 bool open = m_db.isOpen();
00236 if (open)
00237 LOG(VB_GENERAL, LOG_INFO, "MySQL reconnected successfully");
00238
00239 return open;
00240 }
00241
00242
00243
00244
00245
00246 MDBManager::MDBManager()
00247 {
00248 m_nextConnID = 0;
00249 m_connCount = 0;
00250
00251 m_schedCon = NULL;
00252 m_DDCon = NULL;
00253 }
00254
00255 MDBManager::~MDBManager()
00256 {
00257 CloseDatabases();
00258
00259 if (m_connCount != 0 || m_schedCon || m_DDCon)
00260 {
00261 LOG(VB_GENERAL, LOG_CRIT,
00262 "MDBManager exiting with connections still open");
00263 }
00264 #if 0
00265 cout<<"m_connCount: "<<m_connCount<<endl;
00266 cout<<"m_schedCon: "<<m_schedCon<<endl;
00267 cout<<"m_DDCon: "<<m_DDCon<<endl;
00268 #endif
00269 }
00270
00271 MSqlDatabase *MDBManager::popConnection(bool reuse)
00272 {
00273 PurgeIdleConnections(true);
00274
00275 m_lock.lock();
00276
00277 MSqlDatabase *db;
00278
00279 #if REUSE_CONNECTION
00280 if (reuse)
00281 {
00282 db = m_inuse[QThread::currentThread()];
00283 if (db != NULL)
00284 {
00285 m_inuse_count[QThread::currentThread()]++;
00286 m_lock.unlock();
00287 return db;
00288 }
00289 }
00290 #endif
00291
00292 DBList &list = m_pool[QThread::currentThread()];
00293 if (list.isEmpty())
00294 {
00295 db = new MSqlDatabase("DBManager" + QString::number(m_nextConnID++));
00296 ++m_connCount;
00297 LOG(VB_DATABASE, LOG_INFO,
00298 QString("New DB connection, total: %1").arg(m_connCount));
00299 }
00300 else
00301 {
00302 db = list.back();
00303 list.pop_back();
00304 }
00305
00306 #if REUSE_CONNECTION
00307 if (reuse)
00308 {
00309 m_inuse_count[QThread::currentThread()]=1;
00310 m_inuse[QThread::currentThread()] = db;
00311 }
00312 #endif
00313
00314 m_lock.unlock();
00315
00316 db->OpenDatabase();
00317
00318 return db;
00319 }
00320
00321 void MDBManager::pushConnection(MSqlDatabase *db)
00322 {
00323 m_lock.lock();
00324
00325 #if REUSE_CONNECTION
00326 if (db == m_inuse[QThread::currentThread()])
00327 {
00328 int cnt = --m_inuse_count[QThread::currentThread()];
00329 if (cnt > 0)
00330 {
00331 m_lock.unlock();
00332 return;
00333 }
00334 m_inuse[QThread::currentThread()] = NULL;
00335 }
00336 #endif
00337
00338 if (db)
00339 {
00340 db->m_lastDBKick = QDateTime::currentDateTime();
00341 m_pool[QThread::currentThread()].push_front(db);
00342 }
00343
00344 m_lock.unlock();
00345
00346 PurgeIdleConnections(true);
00347 }
00348
00349 void MDBManager::PurgeIdleConnections(bool leaveOne)
00350 {
00351 QMutexLocker locker(&m_lock);
00352
00353 leaveOne = leaveOne || (gCoreContext && gCoreContext->IsUIThread());
00354
00355 QDateTime now = QDateTime::currentDateTime();
00356 DBList &list = m_pool[QThread::currentThread()];
00357 DBList::iterator it = list.begin();
00358
00359 uint purgedConnections = 0, totalConnections = 0;
00360 MSqlDatabase *newDb = NULL;
00361 while (it != list.end())
00362 {
00363 totalConnections++;
00364 if ((*it)->m_lastDBKick.secsTo(now) <= (int)kPurgeTimeout)
00365 {
00366 ++it;
00367 continue;
00368 }
00369
00370
00371
00372 MSqlDatabase *entry = *it;
00373 it = list.erase(it);
00374 --m_connCount;
00375 purgedConnections++;
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387 if (leaveOne && it == list.end() &&
00388 purgedConnections > 0 &&
00389 totalConnections == purgedConnections)
00390 {
00391 newDb = new MSqlDatabase("DBManager" +
00392 QString::number(m_nextConnID++));
00393 ++m_connCount;
00394 LOG(VB_GENERAL, LOG_INFO,
00395 QString("New DB connection, total: %1").arg(m_connCount));
00396 newDb->m_lastDBKick = QDateTime::currentDateTime();
00397 }
00398
00399 LOG(VB_DATABASE, LOG_INFO, "Deleting idle DB connection...");
00400 delete entry;
00401 LOG(VB_DATABASE, LOG_INFO, "Done deleting idle DB connection.");
00402 }
00403 if (newDb)
00404 list.push_front(newDb);
00405
00406 if (purgedConnections)
00407 {
00408 LOG(VB_DATABASE, LOG_INFO,
00409 QString("Purged %1 idle of %2 total DB connections.")
00410 .arg(purgedConnections).arg(totalConnections));
00411 }
00412 }
00413
00414 MSqlDatabase *MDBManager::getStaticCon(MSqlDatabase **dbcon, QString name)
00415 {
00416 if (!dbcon)
00417 return NULL;
00418
00419 if (!*dbcon)
00420 {
00421 *dbcon = new MSqlDatabase(name);
00422 LOG(VB_GENERAL, LOG_INFO, "New static DB connection" + name);
00423 }
00424
00425 (*dbcon)->OpenDatabase();
00426
00427 if (!m_static_pool[QThread::currentThread()].contains(*dbcon))
00428 m_static_pool[QThread::currentThread()].push_back(*dbcon);
00429
00430 return *dbcon;
00431 }
00432
00433 MSqlDatabase *MDBManager::getSchedCon()
00434 {
00435 return getStaticCon(&m_schedCon, "SchedCon");
00436 }
00437
00438 MSqlDatabase *MDBManager::getDDCon()
00439 {
00440 return getStaticCon(&m_DDCon, "DataDirectCon");
00441 }
00442
00443 void MDBManager::CloseDatabases()
00444 {
00445 m_lock.lock();
00446 DBList list = m_pool[QThread::currentThread()];
00447 m_pool[QThread::currentThread()].clear();
00448 m_lock.unlock();
00449
00450 for (DBList::iterator it = list.begin(); it != list.end(); ++it)
00451 {
00452 LOG(VB_DATABASE, LOG_INFO,
00453 "Closing DB connection named '" + (*it)->m_name + "'");
00454 (*it)->m_db.close();
00455 delete (*it);
00456 m_connCount--;
00457 }
00458
00459 m_lock.lock();
00460 DBList &slist = m_static_pool[QThread::currentThread()];
00461 while (!slist.isEmpty())
00462 {
00463 MSqlDatabase *db = slist.takeFirst();
00464 LOG(VB_DATABASE, LOG_INFO,
00465 "Closing DB connection named '" + db->m_name + "'");
00466 db->m_db.close();
00467 delete db;
00468
00469 if (db == m_schedCon)
00470 m_schedCon = NULL;
00471 if (db == m_DDCon)
00472 m_DDCon = NULL;
00473 }
00474 m_lock.unlock();
00475 }
00476
00477
00478
00479
00480 static void InitMSqlQueryInfo(MSqlQueryInfo &qi)
00481 {
00482 qi.db = NULL;
00483 qi.qsqldb = QSqlDatabase();
00484 qi.returnConnection = true;
00485 }
00486
00487
00488 MSqlQuery::MSqlQuery(const MSqlQueryInfo &qi)
00489 : QSqlQuery(QString::null, qi.qsqldb)
00490 {
00491 m_isConnected = false;
00492 m_db = qi.db;
00493 m_returnConnection = qi.returnConnection;
00494
00495 m_isConnected = m_db && m_db->isOpen();
00496
00497 #ifdef DEBUG_QT4_PORT
00498 m_testbindings = QRegExp("(:\\w+)\\W.*\\1\\b");
00499 #endif
00500 }
00501
00502 MSqlQuery::~MSqlQuery()
00503 {
00504 if (m_returnConnection)
00505 {
00506 MDBManager *dbmanager = GetMythDB()->GetDBManager();
00507
00508 if (dbmanager && m_db)
00509 {
00510 dbmanager->pushConnection(m_db);
00511 }
00512 }
00513 }
00514
00515 MSqlQueryInfo MSqlQuery::InitCon(ConnectionReuse _reuse)
00516 {
00517 bool reuse = kNormalConnection == _reuse;
00518 MSqlDatabase *db = GetMythDB()->GetDBManager()->popConnection(reuse);
00519 MSqlQueryInfo qi;
00520
00521 InitMSqlQueryInfo(qi);
00522
00523
00524
00525
00526 if (db->m_db.hostName().isEmpty())
00527 {
00528
00529
00530
00531
00532 GetMythDB()->GetDBManager()->pushConnection(db);
00533 qi.returnConnection = false;
00534 return qi;
00535 }
00536
00537 qi.db = db;
00538 qi.qsqldb = db->db();
00539
00540 db->KickDatabase();
00541
00542 return qi;
00543 }
00544
00545 MSqlQueryInfo MSqlQuery::SchedCon()
00546 {
00547 MSqlDatabase *db = GetMythDB()->GetDBManager()->getSchedCon();
00548 MSqlQueryInfo qi;
00549
00550 InitMSqlQueryInfo(qi);
00551 qi.returnConnection = false;
00552
00553 if (db)
00554 {
00555 qi.db = db;
00556 qi.qsqldb = db->db();
00557
00558 db->KickDatabase();
00559 }
00560
00561 return qi;
00562 }
00563
00564 MSqlQueryInfo MSqlQuery::DDCon()
00565 {
00566 MSqlDatabase *db = GetMythDB()->GetDBManager()->getDDCon();
00567 MSqlQueryInfo qi;
00568
00569 InitMSqlQueryInfo(qi);
00570 qi.returnConnection = false;
00571
00572 if (db)
00573 {
00574 qi.db = db;
00575 qi.qsqldb = db->db();
00576
00577 db->KickDatabase();
00578 }
00579
00580 return qi;
00581 }
00582
00583 bool MSqlQuery::exec()
00584 {
00585 if (!m_db)
00586 {
00587
00588 return false;
00589 }
00590
00591 if (m_last_prepared_query.isEmpty())
00592 {
00593 LOG(VB_GENERAL, LOG_ERR,
00594 "MSqlQuery::exec(void) called without a prepared query.");
00595 return false;
00596 }
00597
00598 #if DEBUG_RECONNECT
00599 if (random() < RAND_MAX / 50)
00600 {
00601 LOG(VB_GENERAL, LOG_INFO,
00602 "MSqlQuery disconnecting DB to test reconnection logic");
00603 m_db->m_db.close();
00604 }
00605 #endif
00606
00607
00608
00609 if (!m_db->isOpen() && !Reconnect())
00610 {
00611 LOG(VB_GENERAL, LOG_INFO, "MySQL server disconnected");
00612 return false;
00613 }
00614
00615 bool result = QSqlQuery::exec();
00616
00617
00618
00619
00620 if (!result && QSqlQuery::lastError().number() == 2006 && Reconnect())
00621 result = QSqlQuery::exec();
00622
00623 if (!result)
00624 {
00625 QString err = MythDB::GetError("MSqlQuery", *this);
00626 MSqlBindings tmp = QSqlQuery::boundValues();
00627 bool has_null_strings = false;
00628 for (MSqlBindings::iterator it = tmp.begin(); it != tmp.end(); ++it)
00629 {
00630 if (it->type() != QVariant::String)
00631 continue;
00632 if (it->isNull() || it->toString().isNull())
00633 {
00634 has_null_strings = true;
00635 *it = QVariant(QString(""));
00636 }
00637 }
00638 if (has_null_strings)
00639 {
00640 bindValues(tmp);
00641 result = QSqlQuery::exec();
00642 }
00643 if (result)
00644 {
00645 LOG(VB_GENERAL, LOG_ERR,
00646 QString("Original query failed, but resend with empty "
00647 "strings in place of NULL strings worked. ") +
00648 "\n" + err);
00649 }
00650 }
00651
00652 if (VERBOSE_LEVEL_CHECK(VB_DATABASE, LOG_DEBUG))
00653 {
00654 QString str = lastQuery();
00655
00656
00657
00658 if (!str.startsWith("INSERT INTO logging "))
00659 {
00660
00661
00662
00663
00664 QMapIterator<QString, QVariant> b = boundValues();
00665 while (b.hasNext())
00666 {
00667 b.next();
00668 str.replace(b.key(), '\'' + b.value().toString() + '\'');
00669 }
00670
00671 LOG(VB_DATABASE, LOG_DEBUG,
00672 QString("MSqlQuery::exec(%1) %2%3")
00673 .arg(m_db->MSqlDatabase::GetConnectionName()).arg(str)
00674 .arg(isSelect() ? QString(" <<<< Returns %1 row(s)")
00675 .arg(size()) : QString()));
00676 }
00677 }
00678
00679 return result;
00680 }
00681
00682 bool MSqlQuery::exec(const QString &query)
00683 {
00684 if (!m_db)
00685 {
00686
00687 return false;
00688 }
00689
00690
00691
00692 if (!m_db->isOpen() && !Reconnect())
00693 {
00694 LOG(VB_GENERAL, LOG_INFO, "MySQL server disconnected");
00695 return false;
00696 }
00697
00698 bool result = QSqlQuery::exec(query);
00699
00700
00701
00702
00703 if (!result && QSqlQuery::lastError().number() == 2006 && Reconnect())
00704 result = QSqlQuery::exec(query);
00705
00706 LOG(VB_DATABASE, LOG_DEBUG,
00707 QString("MSqlQuery::exec(%1) %2%3")
00708 .arg(m_db->MSqlDatabase::GetConnectionName()).arg(query)
00709 .arg(isSelect() ? QString(" <<<< Returns %1 row(s)")
00710 .arg(size()) : QString()));
00711
00712 return result;
00713 }
00714
00715 bool MSqlQuery::seekDebug(const char *type, bool result,
00716 int where, bool relative) const
00717 {
00718 if (result && VERBOSE_LEVEL_CHECK(VB_DATABASE, LOG_DEBUG))
00719 {
00720 QString str;
00721 QSqlRecord rec = record();
00722
00723 for (long int i = 0; i < rec.count(); i++)
00724 {
00725 if (!str.isEmpty())
00726 str.append(", ");
00727
00728 str.append(rec.fieldName(i) + " = " +
00729 value(i).toString());
00730 }
00731
00732 if (QString("seek")==type)
00733 {
00734 LOG(VB_DATABASE, LOG_DEBUG,
00735 QString("MSqlQuery::seek(%1,%2,%3) Result: \"%4\"")
00736 .arg(m_db->MSqlDatabase::GetConnectionName())
00737 .arg(where).arg(relative)
00738 .arg(str));
00739 }
00740 else
00741 {
00742 LOG(VB_DATABASE, LOG_DEBUG,
00743 QString("MSqlQuery::%1(%2) Result: \"%3\"")
00744 .arg(type).arg(m_db->MSqlDatabase::GetConnectionName())
00745 .arg(str));
00746 }
00747 }
00748 return result;
00749 }
00750
00751 bool MSqlQuery::next(void)
00752 {
00753 return seekDebug("next", QSqlQuery::next(), 0, false);
00754 }
00755
00756 bool MSqlQuery::previous(void)
00757 {
00758 return seekDebug("previous", QSqlQuery::previous(), 0, false);
00759 }
00760
00761 bool MSqlQuery::first(void)
00762 {
00763 return seekDebug("first", QSqlQuery::first(), 0, false);
00764 }
00765
00766 bool MSqlQuery::last(void)
00767 {
00768 return seekDebug("last", QSqlQuery::last(), 0, false);
00769 }
00770
00771 bool MSqlQuery::seek(int where, bool relative)
00772 {
00773 return seekDebug("seek", QSqlQuery::seek(where, relative), where, relative);
00774 }
00775
00776 bool MSqlQuery::prepare(const QString& query)
00777 {
00778 if (!m_db)
00779 {
00780
00781 return false;
00782 }
00783
00784 m_last_prepared_query = query;
00785
00786 #ifdef DEBUG_QT4_PORT
00787 if (query.contains(m_testbindings))
00788 {
00789 LOG(VB_GENERAL, LOG_DEBUG,
00790 QString("\n\nQuery contains bind value \"%1\" twice:\n\n\n")
00791 .arg(m_testbindings.cap(1)) + query);
00792 #if 0
00793 exit(1);
00794 #endif
00795 }
00796 #endif
00797
00798
00799
00800 if (!m_db)
00801 {
00802
00803 return false;
00804 }
00805
00806 if (!m_db->isOpen() && !Reconnect())
00807 {
00808 LOG(VB_GENERAL, LOG_INFO, "MySQL server disconnected");
00809 return false;
00810 }
00811
00812 bool ok = QSqlQuery::prepare(query);
00813
00814
00815
00816
00817 if (!ok && QSqlQuery::lastError().number() == 2006 && Reconnect())
00818 ok = true;
00819
00820 if (!ok && !(GetMythDB()->SuppressDBMessages()))
00821 {
00822 LOG(VB_GENERAL, LOG_ERR,
00823 QString("Error preparing query: %1").arg(query));
00824 LOG(VB_GENERAL, LOG_ERR,
00825 MythDB::DBErrorMessage(QSqlQuery::lastError()));
00826 }
00827
00828 return ok;
00829 }
00830
00831 bool MSqlQuery::testDBConnection()
00832 {
00833 MSqlDatabase *db = GetMythDB()->GetDBManager()->popConnection(true);
00834
00835
00836
00837 bool isOpen = db->isOpen();
00838
00839 GetMythDB()->GetDBManager()->pushConnection(db);
00840 return isOpen;
00841 }
00842
00843 void MSqlQuery::bindValue(const QString &placeholder, const QVariant &val)
00844 {
00845 #ifdef DEBUG_QT4_PORT
00846
00847
00848 if (!m_last_prepared_query.contains(placeholder))
00849 {
00850 LOG(VB_GENERAL, LOG_ERR, "Trying to bind a value to placeholder " +
00851 placeholder + " without occurrence in the prepared query."
00852 " Ignoring it.\nQuery was: \"" + m_last_prepared_query + "\"");
00853 return;
00854 }
00855
00856 #endif
00857
00858 QSqlQuery::bindValue(placeholder, val, QSql::In);
00859 }
00860
00861 void MSqlQuery::bindValues(const MSqlBindings &bindings)
00862 {
00863 MSqlBindings::const_iterator it;
00864 for (it = bindings.begin(); it != bindings.end(); ++it)
00865 {
00866 bindValue(it.key(), it.value());
00867 }
00868 }
00869
00870 QVariant MSqlQuery::lastInsertId()
00871 {
00872 return QSqlQuery::lastInsertId();
00873 }
00874
00875 bool MSqlQuery::Reconnect(void)
00876 {
00877 if (!m_db->Reconnect())
00878 return false;
00879 if (!m_last_prepared_query.isEmpty())
00880 {
00881 MSqlBindings tmp = QSqlQuery::boundValues();
00882 if (!QSqlQuery::prepare(m_last_prepared_query))
00883 return false;
00884 bindValues(tmp);
00885 }
00886 return true;
00887 }
00888
00889 void MSqlAddMoreBindings(MSqlBindings &output, MSqlBindings &addfrom)
00890 {
00891 MSqlBindings::Iterator it;
00892 for (it = addfrom.begin(); it != addfrom.end(); ++it)
00893 {
00894 output.insert(it.key(), it.value());
00895 }
00896 }
00897
00898 struct Holder {
00899 Holder( const QString& hldr = QString::null, int pos = -1 )
00900 : holderName( hldr ), holderPos( pos ) {}
00901
00902 bool operator==( const Holder& h ) const
00903 { return h.holderPos == holderPos && h.holderName == holderName; }
00904 bool operator!=( const Holder& h ) const
00905 { return h.holderPos != holderPos || h.holderName != holderName; }
00906 QString holderName;
00907 int holderPos;
00908 };
00909
00910 void MSqlEscapeAsAQuery(QString &query, MSqlBindings &bindings)
00911 {
00912 MSqlQuery result(MSqlQuery::InitCon());
00913
00914 QString q = query;
00915 QRegExp rx(QString::fromLatin1("'[^']*'|:([a-zA-Z0-9_]+)"));
00916
00917 QVector<Holder> holders;
00918
00919 int i = 0;
00920 while ((i = rx.indexIn(q, i)) != -1)
00921 {
00922 if (!rx.cap(1).isEmpty())
00923 holders.append(Holder(rx.cap(0), i));
00924 i += rx.matchedLength();
00925 }
00926
00927 q = query;
00928 QVariant val;
00929 QString holder;
00930
00931 for (i = (int)holders.count() - 1; i >= 0; --i)
00932 {
00933 holder = holders[(uint)i].holderName;
00934 val = bindings[holder];
00935 QSqlField f("", val.type());
00936 if (val.isNull())
00937 f.clear();
00938 else
00939 f.setValue(val);
00940
00941 query = query.replace((uint)holders[(uint)i].holderPos, holder.length(),
00942 result.driver()->formatValue(f));
00943 }
00944 }
00945