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