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