MythTV  master
mythdbcon.cpp
Go to the documentation of this file.
1 #include <unistd.h>
2 
3 // ANSI C
4 #include <cstdlib>
5 
6 // Qt
7 #include <QVector>
8 #include <QSqlDriver>
9 #include <QSemaphore>
10 #include <QSqlError>
11 #include <QSqlField>
12 #include <QSqlRecord>
13 #include <QElapsedTimer>
14 #include <QCoreApplication>
15 
16 // MythTV
17 #include "compat.h"
18 #include "mythdbcon.h"
19 #include "mythdb.h"
20 #include "mythcorecontext.h"
21 #include "mythlogging.h"
22 #include "mythsystemlegacy.h"
23 #include "exitcodes.h"
24 #include "mthread.h"
25 #include "mythdate.h"
26 #include "portchecker.h"
27 #include "mythmiscutil.h"
28 
29 #define DEBUG_RECONNECT 0
30 #if DEBUG_RECONNECT
31 #include <cstdlib>
32 #endif
33 
34 static const uint kPurgeTimeout = 60 * 60;
35 
36 bool TestDatabase(QString dbHostName,
37  QString dbUserName,
38  QString dbPassword,
39  QString dbName,
40  int dbPort)
41 {
42  bool ret = false;
43 
44  if (dbHostName.isEmpty() || dbUserName.isEmpty())
45  return ret;
46 
47  MSqlDatabase *db = new MSqlDatabase("dbtest");
48 
49  if (!db)
50  return ret;
51 
52  DatabaseParams dbparms;
53  dbparms.dbName = dbName;
54  dbparms.dbUserName = dbUserName;
55  dbparms.dbPassword = dbPassword;
56  dbparms.dbHostName = dbHostName;
57  dbparms.dbPort = dbPort;
58 
59  // Just use some sane defaults for these values
60  dbparms.wolEnabled = false;
61  dbparms.wolReconnect = 1;
62  dbparms.wolRetry = 3;
63  dbparms.wolCommand = QString();
64 
65  db->SetDBParams(dbparms);
66 
67  ret = db->OpenDatabase(true);
68 
69  delete db;
70  db = nullptr;
71 
72  return ret;
73 }
74 
76 {
77  m_name = name;
78 
79  if (!QSqlDatabase::isDriverAvailable("QMYSQL"))
80  {
81  LOG(VB_FLUSH, LOG_CRIT, "FATAL: Unable to load the QT mysql driver, is it installed?");
82  exit(GENERIC_EXIT_DB_ERROR); // Exits before we can process the log queue
83  //return;
84  }
85 
86  m_db = QSqlDatabase::addDatabase("QMYSQL", m_name);
87  LOG(VB_DATABASE, LOG_INFO, "Database object created: " + m_name);
88 
89  if (!m_db.isValid() || m_db.isOpenError())
90  {
91  LOG(VB_FLUSH, LOG_CRIT, MythDB::DBErrorMessage(m_db.lastError()));
92  LOG(VB_FLUSH, LOG_CRIT, QString("FATAL: Unable to create database object (%1), the installed QT driver may be invalid.").arg(m_name));
93  exit(GENERIC_EXIT_DB_ERROR); // Exits before we can process the log queue
94  //return;
95  }
96  m_lastDBKick = MythDate::current().addSecs(-60);
97 }
98 
100 {
101  if (m_db.isOpen())
102  {
103  m_db.close();
104  m_db = QSqlDatabase(); // forces a destroy and must be done before
105  // removeDatabase() so that connections
106  // and queries are cleaned up correctly
107  QSqlDatabase::removeDatabase(m_name);
108  LOG(VB_DATABASE, LOG_INFO, "Database object deleted: " + m_name);
109  }
110 }
111 
113 {
114  if (m_db.isValid())
115  {
116  if (m_db.isOpen())
117  return true;
118  }
119  return false;
120 }
121 
122 bool MSqlDatabase::OpenDatabase(bool skipdb)
123 {
124  if (!m_db.isValid())
125  {
126  LOG(VB_GENERAL, LOG_ERR,
127  "MSqlDatabase::OpenDatabase(), db object is not valid!");
128  return false;
129  }
130 
131  bool connected = true;
132 
133  if (!m_db.isOpen())
134  {
135  if (!skipdb)
137  m_db.setDatabaseName(m_dbparms.dbName);
138  m_db.setUserName(m_dbparms.dbUserName);
139  m_db.setPassword(m_dbparms.dbPassword);
140 
141  if (m_dbparms.dbHostName.isEmpty()) // Bootstrapping without a database?
142  {
143  connected = true; // Pretend to be connected
144  return true; // to reduce errors
145  }
146  else
147  {
148  // code to ensure that a link-local ip address has the scope
149  int port = 3306;
150  if (m_dbparms.dbPort)
151  port = m_dbparms.dbPort;
153  m_db.setHostName(m_dbparms.dbHostName);
154  }
155 
156  if (m_dbparms.dbPort)
157  m_db.setPort(m_dbparms.dbPort);
158 
159  // Prefer using the faster localhost connection if using standard
160  // ports, even if the user specified a DBHostName of 127.0.0.1. This
161  // will cause MySQL to use a Unix socket (on *nix) or shared memory (on
162  // Windows) connection.
163  if ((m_dbparms.dbPort == 0 || m_dbparms.dbPort == 3306) &&
164  m_dbparms.dbHostName == "127.0.0.1")
165  m_db.setHostName("localhost");
166 
167  // Default read timeout is 10 mins - set a better value 300 seconds
168  m_db.setConnectOptions(QString("MYSQL_OPT_READ_TIMEOUT=300"));
169 
170  connected = m_db.open();
171 
172  if (!connected && m_dbparms.wolEnabled
174  {
175  int trycount = 0;
176 
177  while (!connected && trycount++ < m_dbparms.wolRetry)
178  {
179  LOG(VB_GENERAL, LOG_INFO,
180  QString("Using WOL to wakeup database server (Try %1 of "
181  "%2)")
182  .arg(trycount).arg(m_dbparms.wolRetry));
183 
185  {
186  LOG(VB_GENERAL, LOG_ERR,
187  QString("Failed to run WOL command '%1'")
188  .arg(m_dbparms.wolCommand));
189  }
190 
192  connected = m_db.open();
193  }
194 
195  if (!connected)
196  {
197  LOG(VB_GENERAL, LOG_ERR,
198  "WOL failed, unable to connect to database!");
199  }
200  }
201  if (connected)
202  {
203  LOG(VB_DATABASE, LOG_INFO,
204  QString("[%1] Connected to database '%2' at host: %3")
205  .arg(m_name)
206  .arg(m_db.databaseName()).arg(m_db.hostName()));
207 
208  InitSessionVars();
209 
210  // WriteDelayed depends on SetHaveDBConnection() and SetHaveSchema()
211  // both being called with true, so order is important here.
213  if (!GetMythDB()->HaveSchema())
214  {
215  // We can't just check the count of QSqlDatabase::tables()
216  // because it returns all tables visible to the user in *all*
217  // databases (not just the current DB).
218  bool have_schema = false;
219  QString sql = "SELECT COUNT(TABLE_NAME) "
220  " FROM INFORMATION_SCHEMA.TABLES "
221  " WHERE TABLE_SCHEMA = DATABASE() "
222  " AND TABLE_TYPE = 'BASE TABLE';";
223  // We can't use MSqlQuery to determine if we have a schema,
224  // since it will open a new connection, which will try to check
225  // if we have a schema
226  QSqlQuery query = m_db.exec(sql); // don't convert to MSqlQuery
227  if (query.next())
228  have_schema = query.value(0).toInt() > 1;
229  GetMythDB()->SetHaveSchema(have_schema);
230  }
232  }
233  }
234 
235  if (!connected)
236  {
237  GetMythDB()->SetHaveDBConnection(false);
238  LOG(VB_GENERAL, LOG_ERR, QString("[%1] Unable to connect to database!").arg(m_name));
239  LOG(VB_GENERAL, LOG_ERR, MythDB::DBErrorMessage(m_db.lastError()));
240  }
241 
242  return connected;
243 }
244 
246 {
247  m_lastDBKick = MythDate::current().addSecs(-60);
248 
249  if (!m_db.isOpen())
250  m_db.open();
251 
252  return m_db.isOpen();
253 }
254 
256 {
257  m_db.close();
258  m_db.open();
259 
260  bool open = m_db.isOpen();
261  if (open)
262  {
263  LOG(VB_GENERAL, LOG_INFO, "MySQL reconnected successfully");
264  InitSessionVars();
265  }
266 
267  return open;
268 }
269 
271 {
272  // Make sure NOW() returns time in UTC...
273  m_db.exec("SET @@session.time_zone='+00:00'");
274  // Disable strict mode
275  m_db.exec("SET @@session.sql_mode=''");
276 }
277 
278 // -----------------------------------------------------------------------
279 
280 
281 
283 {
284  m_nextConnID = 0;
285  m_connCount = 0;
286 
287  m_schedCon = nullptr;
288  m_DDCon = nullptr;
289 }
290 
292 {
293  CloseDatabases();
294 
295  if (m_connCount != 0 || m_schedCon || m_DDCon)
296  {
297  LOG(VB_GENERAL, LOG_CRIT,
298  "MDBManager exiting with connections still open");
299  }
300 #if 0 /* some post logStop() debugging... */
301  cout<<"m_connCount: "<<m_connCount<<endl;
302  cout<<"m_schedCon: "<<m_schedCon<<endl;
303  cout<<"m_DDCon: "<<m_DDCon<<endl;
304 #endif
305 }
306 
308 {
309  PurgeIdleConnections(true);
310 
311  m_lock.lock();
312 
313  MSqlDatabase *db;
314 
315 #if REUSE_CONNECTION
316  if (reuse)
317  {
318  db = m_inuse[QThread::currentThread()];
319  if (db != nullptr)
320  {
321  m_inuse_count[QThread::currentThread()]++;
322  m_lock.unlock();
323  return db;
324  }
325  }
326 #endif
327 
328  DBList &list = m_pool[QThread::currentThread()];
329  if (list.isEmpty())
330  {
331  db = new MSqlDatabase("DBManager" + QString::number(m_nextConnID++));
332  ++m_connCount;
333  LOG(VB_DATABASE, LOG_INFO,
334  QString("New DB connection, total: %1").arg(m_connCount));
335  }
336  else
337  {
338  db = list.back();
339  list.pop_back();
340  }
341 
342 #if REUSE_CONNECTION
343  if (reuse)
344  {
345  m_inuse_count[QThread::currentThread()]=1;
346  m_inuse[QThread::currentThread()] = db;
347  }
348 #endif
349 
350  m_lock.unlock();
351 
352  db->OpenDatabase();
353 
354  return db;
355 }
356 
358 {
359  m_lock.lock();
360 
361 #if REUSE_CONNECTION
362  if (db == m_inuse[QThread::currentThread()])
363  {
364  int cnt = --m_inuse_count[QThread::currentThread()];
365  if (cnt > 0)
366  {
367  m_lock.unlock();
368  return;
369  }
370  m_inuse[QThread::currentThread()] = nullptr;
371  }
372 #endif
373 
374  if (db)
375  {
377  m_pool[QThread::currentThread()].push_front(db);
378  }
379 
380  m_lock.unlock();
381 
382  PurgeIdleConnections(true);
383 }
384 
386 {
387  QMutexLocker locker(&m_lock);
388 
389  leaveOne = leaveOne || (gCoreContext && gCoreContext->IsUIThread());
390 
391  QDateTime now = MythDate::current();
392  DBList &list = m_pool[QThread::currentThread()];
393  DBList::iterator it = list.begin();
394 
395  uint purgedConnections = 0, totalConnections = 0;
396  MSqlDatabase *newDb = nullptr;
397  while (it != list.end())
398  {
399  totalConnections++;
400  if ((*it)->m_lastDBKick.secsTo(now) <= (int)kPurgeTimeout)
401  {
402  ++it;
403  continue;
404  }
405 
406  // This connection has not been used in the kPurgeTimeout
407  // seconds close it.
408  MSqlDatabase *entry = *it;
409  it = list.erase(it);
410  --m_connCount;
411  purgedConnections++;
412 
413  // Qt's MySQL driver apparently keeps track of the number of
414  // open DB connections, and when it hits 0, calls
415  // my_thread_global_end(). The mysql library then assumes the
416  // application is ending and that all threads that created DB
417  // connections have already exited. This is rarely true, and
418  // may result in the mysql library pausing 5 seconds and
419  // printing a message like "Error in my_thread_global_end(): 1
420  // threads didn't exit". This workaround simply creates an
421  // extra DB connection before all pooled connections are
422  // purged so that my_thread_global_end() won't be called.
423  if (leaveOne && it == list.end() &&
424  purgedConnections > 0 &&
425  totalConnections == purgedConnections)
426  {
427  newDb = new MSqlDatabase("DBManager" +
428  QString::number(m_nextConnID++));
429  ++m_connCount;
430  LOG(VB_GENERAL, LOG_INFO,
431  QString("New DB connection, total: %1").arg(m_connCount));
432  newDb->m_lastDBKick = MythDate::current();
433  }
434 
435  LOG(VB_DATABASE, LOG_INFO, "Deleting idle DB connection...");
436  delete entry;
437  LOG(VB_DATABASE, LOG_INFO, "Done deleting idle DB connection.");
438  }
439  if (newDb)
440  list.push_front(newDb);
441 
442  if (purgedConnections)
443  {
444  LOG(VB_DATABASE, LOG_INFO,
445  QString("Purged %1 idle of %2 total DB connections.")
446  .arg(purgedConnections).arg(totalConnections));
447  }
448 }
449 
451 {
452  if (!dbcon)
453  return nullptr;
454 
455  if (!*dbcon)
456  {
457  *dbcon = new MSqlDatabase(name);
458  LOG(VB_GENERAL, LOG_INFO, "New static DB connection" + name);
459  }
460 
461  (*dbcon)->OpenDatabase();
462 
463  if (!m_static_pool[QThread::currentThread()].contains(*dbcon))
464  m_static_pool[QThread::currentThread()].push_back(*dbcon);
465 
466  return *dbcon;
467 }
468 
470 {
471  return getStaticCon(&m_schedCon, "SchedCon");
472 }
473 
475 {
476  return getStaticCon(&m_DDCon, "DataDirectCon");
477 }
478 
480 {
481  m_lock.lock();
482  DBList list = m_pool[QThread::currentThread()];
483  m_pool[QThread::currentThread()].clear();
484  m_lock.unlock();
485 
486  for (DBList::iterator it = list.begin(); it != list.end(); ++it)
487  {
488  LOG(VB_DATABASE, LOG_INFO,
489  "Closing DB connection named '" + (*it)->m_name + "'");
490  (*it)->m_db.close();
491  delete (*it);
492  m_connCount--;
493  }
494 
495  m_lock.lock();
496  DBList &slist = m_static_pool[QThread::currentThread()];
497  while (!slist.isEmpty())
498  {
499  MSqlDatabase *db = slist.takeFirst();
500  LOG(VB_DATABASE, LOG_INFO,
501  "Closing DB connection named '" + db->m_name + "'");
502  db->m_db.close();
503  delete db;
504 
505  if (db == m_schedCon)
506  m_schedCon = nullptr;
507  if (db == m_DDCon)
508  m_DDCon = nullptr;
509  }
510  m_lock.unlock();
511 }
512 
513 
514 // -----------------------------------------------------------------------
515 
517 {
518  qi.db = nullptr;
519  qi.qsqldb = QSqlDatabase();
520  qi.returnConnection = true;
521 }
522 
523 
525  : QSqlQuery(QString(), qi.qsqldb)
526 {
527  m_isConnected = false;
528  m_db = qi.db;
530 
531  m_isConnected = m_db && m_db->isOpen();
532 }
533 
535 {
536  if (m_returnConnection)
537  {
538  MDBManager *dbmanager = GetMythDB()->GetDBManager();
539 
540  if (dbmanager && m_db)
541  {
542  dbmanager->pushConnection(m_db);
543  }
544  }
545 }
546 
548 {
549  bool reuse = kNormalConnection == _reuse;
551  MSqlQueryInfo qi;
552 
553  InitMSqlQueryInfo(qi);
554 
555 
556  // Bootstrapping without a database?
557  //if (db->pretendHaveDB)
558  if (db->m_db.hostName().isEmpty())
559  {
560  // Return an invalid database so that QSqlQuery does nothing.
561  // Also works around a Qt4 bug where QSqlQuery::~QSqlQuery
562  // calls QMYSQLResult::cleanup() which uses mysql_next_result()
563 
565  qi.returnConnection = false;
566  return qi;
567  }
568 
569  qi.db = db;
570  qi.qsqldb = db->db();
571 
572  db->KickDatabase();
573 
574  return qi;
575 }
576 
578 {
580  MSqlQueryInfo qi;
581 
582  InitMSqlQueryInfo(qi);
583  qi.returnConnection = false;
584 
585  if (db)
586  {
587  qi.db = db;
588  qi.qsqldb = db->db();
589 
590  db->KickDatabase();
591  }
592 
593  return qi;
594 }
595 
597 {
599  MSqlQueryInfo qi;
600 
601  InitMSqlQueryInfo(qi);
602  qi.returnConnection = false;
603 
604  if (db)
605  {
606  qi.db = db;
607  qi.qsqldb = db->db();
608 
609  db->KickDatabase();
610  }
611 
612  return qi;
613 }
614 
616 {
617  if (!m_db)
618  {
619  // Database structure's been deleted
620  return false;
621  }
622 
623  if (m_last_prepared_query.isEmpty())
624  {
625  LOG(VB_GENERAL, LOG_ERR,
626  "MSqlQuery::exec(void) called without a prepared query.");
627  return false;
628  }
629 
630 #if DEBUG_RECONNECT
631  if (random() < RAND_MAX / 50)
632  {
633  LOG(VB_GENERAL, LOG_INFO,
634  "MSqlQuery disconnecting DB to test reconnection logic");
635  m_db->m_db.close();
636  }
637 #endif
638 
639  // Database connection down. Try to restart it, give up if it's still
640  // down
641  if (!m_db->isOpen() && !Reconnect())
642  {
643  LOG(VB_GENERAL, LOG_INFO, "MySQL server disconnected");
644  return false;
645  }
646 
647  QElapsedTimer timer;
648  timer.start();
649 
650  bool result = QSqlQuery::exec();
651  qint64 elapsed = timer.elapsed();
652 
653  // if the query failed with "MySQL server has gone away"
654  // Close and reopen the database connection and retry the query if it
655  // connects again
656  if (!result
657  && QSqlQuery::lastError().nativeErrorCode() == "2006"
658  && Reconnect())
659  result = QSqlQuery::exec();
660 
661  if (!result)
662  {
663  QString err = MythDB::GetError("MSqlQuery", *this);
664  MSqlBindings tmp = QSqlQuery::boundValues();
665  bool has_null_strings = false;
666  for (MSqlBindings::iterator it = tmp.begin(); it != tmp.end(); ++it)
667  {
668  if (it->type() != QVariant::String)
669  continue;
670  if (it->isNull() || it->toString().isNull())
671  {
672  has_null_strings = true;
673  *it = QVariant(QString(""));
674  }
675  }
676  if (has_null_strings)
677  {
678  bindValues(tmp);
679  timer.restart();
680  result = QSqlQuery::exec();
681  elapsed = timer.elapsed();
682  }
683  if (result)
684  {
685  LOG(VB_GENERAL, LOG_ERR,
686  QString("Original query failed, but resend with empty "
687  "strings in place of NULL strings worked. ") +
688  "\n" + err);
689  }
690  }
691 
692  if (VERBOSE_LEVEL_CHECK(VB_DATABASE, LOG_INFO))
693  {
694  QString str = lastQuery();
695 
696  // Database logging will cause an infinite loop here if not filtered
697  // out
698  if (!str.startsWith("INSERT INTO logging "))
699  {
700  // Sadly, neither executedQuery() nor lastQuery() display
701  // the values in bound queries against a MySQL5 database.
702  // So, replace the named placeholders with their values.
703 
704  QMapIterator<QString, QVariant> b = boundValues();
705  while (b.hasNext())
706  {
707  b.next();
708  str.replace(b.key(), '\'' + b.value().toString() + '\'');
709  }
710 
711  LOG(VB_DATABASE, LOG_INFO,
712  QString("MSqlQuery::exec(%1) %2%3%4")
713  .arg(m_db->MSqlDatabase::GetConnectionName()).arg(str)
714  .arg(QString(" <<<< Took %1ms").arg(QString::number(elapsed)))
715  .arg(isSelect() ? QString(", Returned %1 row(s)")
716  .arg(size()) : QString()));
717  }
718  }
719 
720  return result;
721 }
722 
723 bool MSqlQuery::exec(const QString &query)
724 {
725  if (!m_db)
726  {
727  // Database structure's been deleted
728  return false;
729  }
730 
731  // Database connection down. Try to restart it, give up if it's still
732  // down
733  if (!m_db->isOpen() && !Reconnect())
734  {
735  LOG(VB_GENERAL, LOG_INFO, "MySQL server disconnected");
736  return false;
737  }
738 
739  bool result = QSqlQuery::exec(query);
740 
741  // if the query failed with "MySQL server has gone away"
742  // Close and reopen the database connection and retry the query if it
743  // connects again
744  if (!result
745  && QSqlQuery::lastError().nativeErrorCode() == "2006"
746  && Reconnect())
747  result = QSqlQuery::exec(query);
748 
749  LOG(VB_DATABASE, LOG_INFO,
750  QString("MSqlQuery::exec(%1) %2%3")
751  .arg(m_db->MSqlDatabase::GetConnectionName()).arg(query)
752  .arg(isSelect() ? QString(" <<<< Returns %1 row(s)")
753  .arg(size()) : QString()));
754 
755  return result;
756 }
757 
758 bool MSqlQuery::seekDebug(const char *type, bool result,
759  int where, bool relative) const
760 {
761  if (result && VERBOSE_LEVEL_CHECK(VB_DATABASE, LOG_DEBUG))
762  {
763  QString str;
764  QSqlRecord rec = record();
765 
766  for (long int i = 0; i < rec.count(); i++)
767  {
768  if (!str.isEmpty())
769  str.append(", ");
770 
771  str.append(rec.fieldName(i) + " = " +
772  value(i).toString());
773  }
774 
775  if (QString("seek")==type)
776  {
777  LOG(VB_DATABASE, LOG_DEBUG,
778  QString("MSqlQuery::seek(%1,%2,%3) Result: \"%4\"")
779  .arg(m_db->MSqlDatabase::GetConnectionName())
780  .arg(where).arg(relative)
781  .arg(str));
782  }
783  else
784  {
785  LOG(VB_DATABASE, LOG_DEBUG,
786  QString("MSqlQuery::%1(%2) Result: \"%3\"")
787  .arg(type).arg(m_db->MSqlDatabase::GetConnectionName())
788  .arg(str));
789  }
790  }
791  return result;
792 }
793 
794 bool MSqlQuery::next(void)
795 {
796  return seekDebug("next", QSqlQuery::next(), 0, false);
797 }
798 
800 {
801  return seekDebug("previous", QSqlQuery::previous(), 0, false);
802 }
803 
805 {
806  return seekDebug("first", QSqlQuery::first(), 0, false);
807 }
808 
809 bool MSqlQuery::last(void)
810 {
811  return seekDebug("last", QSqlQuery::last(), 0, false);
812 }
813 
814 bool MSqlQuery::seek(int where, bool relative)
815 {
816  return seekDebug("seek", QSqlQuery::seek(where, relative), where, relative);
817 }
818 
819 bool MSqlQuery::prepare(const QString& query)
820 {
821  if (!m_db)
822  {
823  // Database structure's been deleted
824  return false;
825  }
826 
827  m_last_prepared_query = query;
828 
829  if (!m_db->isOpen() && !Reconnect())
830  {
831  LOG(VB_GENERAL, LOG_INFO, "MySQL server disconnected");
832  return false;
833  }
834 
835  // QT docs indicate that there are significant speed ups and a reduction
836  // in memory usage by enabling forward-only cursors
837  //
838  // Unconditionally enable this since all existing uses of the database
839  // iterate forward over the result set.
840  setForwardOnly(true);
841 
842  bool ok = QSqlQuery::prepare(query);
843 
844  // if the prepare failed with "MySQL server has gone away"
845  // Close and reopen the database connection and retry the query if it
846  // connects again
847  if (!ok
848  && QSqlQuery::lastError().nativeErrorCode() == "2006"
849  && Reconnect())
850  ok = true;
851 
852  if (!ok && !(GetMythDB()->SuppressDBMessages()))
853  {
854  LOG(VB_GENERAL, LOG_ERR,
855  QString("Error preparing query: %1").arg(query));
856  LOG(VB_GENERAL, LOG_ERR,
857  MythDB::DBErrorMessage(QSqlQuery::lastError()));
858  }
859 
860  return ok;
861 }
862 
864 {
866 
867  // popConnection() has already called OpenDatabase(),
868  // so we only have to check if it was successful:
869  bool isOpen = db->isOpen();
870 
872  return isOpen;
873 }
874 
875 void MSqlQuery::bindValue(const QString &placeholder, const QVariant &val)
876 {
877  QSqlQuery::bindValue(placeholder, val, QSql::In);
878 }
879 
880 void MSqlQuery::bindValues(const MSqlBindings &bindings)
881 {
882  MSqlBindings::const_iterator it;
883  for (it = bindings.begin(); it != bindings.end(); ++it)
884  {
885  bindValue(it.key(), it.value());
886  }
887 }
888 
890 {
891  return QSqlQuery::lastInsertId();
892 }
893 
895 {
896  if (!m_db->Reconnect())
897  return false;
898  if (!m_last_prepared_query.isEmpty())
899  {
900  MSqlBindings tmp = QSqlQuery::boundValues();
901  if (!QSqlQuery::prepare(m_last_prepared_query))
902  return false;
903  bindValues(tmp);
904  }
905  return true;
906 }
907 
909 {
910  MSqlBindings::Iterator it;
911  for (it = addfrom.begin(); it != addfrom.end(); ++it)
912  {
913  output.insert(it.key(), it.value());
914  }
915 }
916 
917 struct Holder {
918  Holder( const QString& hldr = QString(), int pos = -1 )
919  : holderName( hldr ), holderPos( pos ) {}
920 
921  bool operator==( const Holder& h ) const
922  { return h.holderPos == holderPos && h.holderName == holderName; }
923  bool operator!=( const Holder& h ) const
924  { return h.holderPos != holderPos || h.holderName != holderName; }
925  QString holderName;
927 };
928 
929 void MSqlEscapeAsAQuery(QString &query, MSqlBindings &bindings)
930 {
931  MSqlQuery result(MSqlQuery::InitCon());
932 
933  QString q = query;
934  QRegExp rx(QString::fromLatin1("'[^']*'|:([a-zA-Z0-9_]+)"));
935 
936  QVector<Holder> holders;
937 
938  int i = 0;
939  while ((i = rx.indexIn(q, i)) != -1)
940  {
941  if (!rx.cap(1).isEmpty())
942  holders.append(Holder(rx.cap(0), i));
943  i += rx.matchedLength();
944  }
945 
946  QVariant val;
947  QString holder;
948 
949  for (i = holders.count() - 1; i >= 0; --i)
950  {
951  holder = holders[(uint)i].holderName;
952  val = bindings[holder];
953  QSqlField f("", val.type());
954  if (val.isNull())
955  f.clear();
956  else
957  f.setValue(val);
958 
959  query = query.replace((uint)holders[(uint)i].holderPos, holder.length(),
960  result.driver()->formatValue(f));
961  }
962 }
void InitSessionVars(void)
Definition: mythdbcon.cpp:270
#define GENERIC_EXIT_DB_ERROR
Database error.
Definition: exitcodes.h:17
QSqlDatabase wrapper, used by MSqlQuery. Do not use directly.
Definition: mythdbcon.h:26
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:794
DatabaseParams GetDatabaseParams(void) const
Definition: mythdb.cpp:199
bool wolEnabled
true if wake-on-lan params are used
Definition: mythdbparams.h:32
QSqlDatabase m_db
Definition: mythdbcon.h:47
bool operator==(const Holder &h) const
Definition: mythdbcon.cpp:921
void bindValue(const QString &placeholder, const QVariant &val)
Definition: mythdbcon.cpp:875
QString wolCommand
command to use for wake-on-lan
Definition: mythdbparams.h:35
DatabaseParams m_dbparms
Definition: mythdbcon.h:49
struct exc__state * last
Definition: pxsup2dast.c:98
QString dbName
database name
Definition: mythdbparams.h:26
bool OpenDatabase(bool skipdb=false)
Definition: mythdbcon.cpp:122
bool last(void)
Wrap QSqlQuery::last() so we can display the query results.
Definition: mythdbcon.cpp:809
QHash< QThread *, MSqlDatabase * > m_inuse
Definition: mythdbcon.h:77
QList< MSqlDatabase * > DBList
Definition: mythdbcon.h:74
void WriteDelayedSettings(void)
Definition: mythdb.cpp:898
MSqlDatabase * m_schedCon
Definition: mythdbcon.h:84
MSqlDatabase * getStaticCon(MSqlDatabase **dbcon, QString name)
Definition: mythdbcon.cpp:450
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
QHash< QThread *, DBList > m_static_pool
Definition: mythdbcon.h:86
int size(void) const
Definition: mythdbcon.h:187
MSqlDatabase * popConnection(bool reuse)
Definition: mythdbcon.cpp:307
Holder(const QString &hldr=QString(), int pos=-1)
Definition: mythdbcon.cpp:918
MDBManager(void)
Definition: mythdbcon.cpp:282
bool MythWakeup(const QString &wakeUpCommand, uint flags, uint timeout)
static const uint kPurgeTimeout
Definition: mythdbcon.cpp:34
MSqlDatabase * db
Definition: mythdbcon.h:92
unsigned int uint
Definition: compat.h:140
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
QSqlDatabase db(void) const
Definition: mythdbcon.h:41
QString holderName
Definition: mythdbcon.cpp:925
QHash< QThread *, int > m_inuse_count
Definition: mythdbcon.h:78
static guint32 * tmp
Definition: goom_core.c:35
unsigned sleep(unsigned int x)
Definition: compat.h:152
QString dbPassword
DB password.
Definition: mythdbparams.h:25
static bool resolveLinkLocal(QString &host, int port, int timeLimit=30000)
Convenience method to resolve link-local address.
unsigned char b
Definition: ParseText.cpp:340
QVariant value(int i) const
Definition: mythdbcon.h:182
QMutex m_lock
Definition: mythdbcon.h:73
QString dbUserName
DB user name.
Definition: mythdbparams.h:24
QString m_name
Definition: mythdbcon.h:46
bool Reconnect(void)
Reconnects server and re-prepares and re-binds the last prepared query.
Definition: mythdbcon.cpp:894
~MSqlDatabase(void)
Definition: mythdbcon.cpp:99
void MSqlEscapeAsAQuery(QString &query, MSqlBindings &bindings)
Given a partial query string and a bindings object, escape the string.
Definition: mythdbcon.cpp:929
int holderPos
Definition: mythdbcon.cpp:926
QVariant lastInsertId()
Return the id of the last inserted row.
Definition: mythdbcon.cpp:889
bool m_returnConnection
Definition: mythdbcon.h:224
~MSqlQuery()
Returns connection to pool.
Definition: mythdbcon.cpp:534
static void InitMSqlQueryInfo(MSqlQueryInfo &qi)
Definition: mythdbcon.cpp:516
bool returnConnection
Definition: mythdbcon.h:94
#define VERBOSE_LEVEL_CHECK(_MASK_, _LEVEL_)
Definition: mythlogging.h:24
bool operator!=(const Holder &h) const
Definition: mythdbcon.cpp:923
QSqlRecord record(void) const
Definition: mythdbcon.h:189
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
void SetHaveDBConnection(bool connected)
Set a flag indicating we have successfully connected to the database.
Definition: mythdb.cpp:916
void pushConnection(MSqlDatabase *db)
Definition: mythdbcon.cpp:357
static MSqlQueryInfo DDCon()
Returns dedicated connection. (Required for using temporary SQL tables.)
Definition: mythdbcon.cpp:596
static bool testDBConnection()
Checks DB connection + login (login info via Mythcontext)
Definition: mythdbcon.cpp:863
static MSqlQueryInfo SchedCon()
Returns dedicated connection. (Required for using temporary SQL tables.)
Definition: mythdbcon.cpp:577
static MSqlQueryInfo InitCon(ConnectionReuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:547
bool Reconnect(void)
Definition: mythdbcon.cpp:255
const char * name
Definition: ParseText.cpp:339
QString m_last_prepared_query
Definition: mythdbcon.h:225
MSqlDatabase(const QString &name)
Definition: mythdbcon.cpp:75
int wolReconnect
seconds to wait for reconnect
Definition: mythdbparams.h:33
bool previous(void)
Wrap QSqlQuery::previous() so we can display the query results.
Definition: mythdbcon.cpp:799
bool seekDebug(const char *type, bool result, int where, bool relative) const
Definition: mythdbcon.cpp:758
QMap< QString, QVariant > boundValues(void) const
Definition: mythdbcon.h:184
int m_connCount
Definition: mythdbcon.h:82
MSqlDatabase Info, used by MSqlQuery. Do not use directly.
Definition: mythdbcon.h:90
int m_nextConnID
Definition: mythdbcon.h:81
bool first(void)
Wrap QSqlQuery::first() so we can display the query results.
Definition: mythdbcon.cpp:804
MSqlQuery(const MSqlQueryInfo &qi)
Get DB connection from pool.
Definition: mythdbcon.cpp:524
PictureAttribute next(PictureAttributeSupported supported, PictureAttribute attribute)
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:819
MSqlDatabase * getSchedCon(void)
Definition: mythdbcon.cpp:469
Structure containing the basic Database parameters.
Definition: mythdbparams.h:9
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
DB connection pool, used by MSqlQuery. Do not use directly.
Definition: mythdbcon.h:53
QString dbHostName
database server
Definition: mythdbparams.h:21
void SetHaveSchema(bool schema)
Set a flag indicating that we have discovered tables and that this therefore not a new empty database...
Definition: mythdb.cpp:925
~MDBManager(void)
Definition: mythdbcon.cpp:291
MSqlDatabase * getDDCon(void)
Definition: mythdbcon.cpp:474
bool KickDatabase(void)
Definition: mythdbcon.cpp:245
void bindValues(const MSqlBindings &bindings)
Add all the bindings in the passed in bindings.
Definition: mythdbcon.cpp:880
MDBManager * GetDBManager(void)
Definition: mythdb.cpp:115
bool seek(int, bool relative=false)
Wrap QSqlQuery::seek(int,bool)
Definition: mythdbcon.cpp:814
QDateTime m_lastDBKick
Definition: mythdbcon.h:48
bool TestDatabase(QString dbHostName, QString dbUserName, QString dbPassword, QString dbName, int dbPort)
Definition: mythdbcon.cpp:36
MSqlDatabase * m_db
Definition: mythdbcon.h:222
bool IsWOLAllowed() const
void MSqlAddMoreBindings(MSqlBindings &output, MSqlBindings &addfrom)
Add the entries in addfrom to the map in output.
Definition: mythdbcon.cpp:908
const QSqlDriver * driver(void) const
Definition: mythdbcon.h:193
static QString DBErrorMessage(const QSqlError &err)
Definition: mythdb.cpp:184
MSqlDatabase * m_DDCon
Definition: mythdbcon.h:85
void SetDBParams(const DatabaseParams &params)
Definition: mythdbcon.h:35
QMap< QString, QVariant > MSqlBindings
typedef for a map of string -> string bindings for generic queries.
Definition: mythdbcon.h:98
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:615
static long int random(void)
Definition: compat.h:147
MythDB * GetMythDB(void)
Definition: mythdb.cpp:46
QHash< QThread *, DBList > m_pool
Definition: mythdbcon.h:75
int dbPort
database port
Definition: mythdbparams.h:23
QSqlDatabase qsqldb
Definition: mythdbcon.h:93
bool isOpen(void)
Definition: mythdbcon.cpp:112
static QString GetError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:163
void CloseDatabases(void)
Definition: mythdbcon.cpp:479
bool m_isConnected
Definition: mythdbcon.h:223
int wolRetry
times to retry to reconnect
Definition: mythdbparams.h:34
void PurgeIdleConnections(bool leaveOne=false)
Definition: mythdbcon.cpp:385
#define output
void setForwardOnly(bool f)
Definition: mythdbcon.h:191