MythTV  master
mythdb.cpp
Go to the documentation of this file.
1 #include <vector>
2 
3 #include <QReadWriteLock>
4 #include <QTextStream>
5 #include <QSqlError>
6 #include <QMutex>
7 #include <QFile>
8 #include <QHash>
9 #include <QDir>
10 #include <QRegularExpression>
11 #include <QStandardPaths>
12 
13 #include "mythconfig.h"
14 #include "mythdb.h"
15 #include "mythdbcon.h"
16 #include "mythlogging.h"
17 #include "mythdirs.h"
18 #include "mythcorecontext.h"
19 #include "mythmiscutil.h"
20 
21 static MythDB *mythdb = nullptr;
22 static QMutex dbLock;
23 
24 // For thread safety reasons this is not a QString
25 const char * const kSentinelValue { "<settings_sentinel_value>" };
26 const char * const kClearSettingValue { "<clear_setting_value>" };
27 
28 MythDB *MythDB::getMythDB(void)
29 {
30  if (mythdb)
31  return mythdb;
32 
33  dbLock.lock();
34  if (!mythdb)
35  mythdb = new MythDB();
36  dbLock.unlock();
37 
38  return mythdb;
39 }
40 
42 {
43  dbLock.lock();
44  delete mythdb;
45  mythdb = nullptr;
46  dbLock.unlock();
47 }
48 
49 MythDB *GetMythDB(void)
50 {
51  return MythDB::getMythDB();
52 }
53 
54 void DestroyMythDB(void)
55 {
57 }
58 
59 MythDB *GetMythTestDB(const QString& testname)
60 {
61  auto * db = MythDB::getMythDB();
62 
63  DatabaseParams params {};
64  params.m_dbHostName = "localhost";
65  params.m_dbHostPing = false;
66 #if CONFIG_DEBUGTYPE
67  params.m_dbName =
68  QStandardPaths::writableLocation(QStandardPaths::TempLocation) +
69  QDir::separator() +
70  QString("mythtv_%1.%2.sqlite3")
71  .arg(testname).arg(MythRandom(),8,16,QLatin1Char('0'));
72 #else
73  Q_UNUSED(testname);
74  params.m_dbName = ":memory:";
75 #endif
76  params.m_dbType = "QSQLITE";
77  db->SetDatabaseParams(params);
78 
79  return db;
80 }
81 
83 {
84  QString m_key;
85  QString m_value;
86  QString m_host;
87 };
88 
89 using SettingsMap = QHash<QString,QString>;
90 
92 {
93  public:
94  MythDBPrivate();
96 
98  QString m_localhostname;
100 
101  bool m_ignoreDatabase {false};
102  bool m_suppressDBMessages {true};
103 
104  QReadWriteLock m_settingsCacheLock;
105  volatile bool m_useSettingsCache {false};
112  QList<SingleSetting> m_delayedSettings;
113 
114  bool m_haveDBConnection {false};
115  bool m_haveSchema {false};
116 };
117 
118 static const int settings_reserve = 61;
119 
121 {
122  m_localhostname.clear();
124 }
125 
127 {
128  LOG(VB_DATABASE, LOG_INFO, "Destroying MythDBPrivate");
129 }
130 
132 {
133  d = new MythDBPrivate();
134 }
135 
137 {
138  delete d;
139 }
140 
142 {
143  return &(d->m_dbmanager);
144 }
145 
146 QString MythDB::toCommaList(const QMap<QString, QVariant> &bindings,
147  uint indent, uint maxColumn)
148 {
149  QMap<QString, QVariant>::const_iterator it = bindings.begin();
150  if (it == bindings.end())
151  return "";
152 
153  uint curColumn = indent;
154  QString str = QString("%1").arg("", indent);
155  for (; it != bindings.end(); ++it)
156  {
157  QString val = (*it).toString();
158  if ((*it).isNull())
159  {
160  val = "NULL";
161  }
162  else if (
163 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
164  it->type() == QVariant::String
165 #else
166  it->typeId() == QMetaType::QString
167 #endif
168  )
169  {
170  val = (it->toString().isNull()) ?
171  "NULL" : QString("\"%1\"").arg(val);
172  }
173  const QString curBinding = it.key() + '=' + val + ',';
174  if ((curColumn > indent) &&
175  ((curBinding.length() + curColumn) > maxColumn))
176  {
177  str += '\n';
178  str += QString("%1").arg("", indent);
179  curColumn = indent;
180  }
181  if (curColumn > indent)
182  {
183  str += ' ';
184  curColumn++;
185  }
186  str += curBinding;
187  curColumn += curBinding.length();
188  }
189  str = str.left(str.length() - 1); // remove trailing comma.
190  str += '\n';
191 
192  return str;
193 }
194 
195 QString MythDB::GetError(const QString &where, const MSqlQuery &query)
196 {
197  QString str = QString("DB Error (%1):\n").arg(where);
198 
199  str += "Query was:\n";
200  str += query.executedQuery() + '\n';
201 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
202  QString tmp = toCommaList(query.boundValues());
203 #else
204  QVariantList numberedBindings = query.boundValues();
205  QMap<QString, QVariant> namedBindings;
206  static const QRegularExpression placeholders { "(:\\w+)" };
207  auto iter = placeholders.globalMatch(str);
208  while (iter.hasNext())
209  {
210  auto match = iter.next();
211  namedBindings[match.captured()] = numberedBindings.isEmpty()
212  ? QString("INVALID")
213  : numberedBindings.takeFirst();
214  }
215  QString tmp = toCommaList(namedBindings);
216 #endif
217  if (!tmp.isEmpty())
218  {
219  str += "Bindings were:\n";
220  str += tmp;
221  }
222  str += DBErrorMessage(query.lastError());
223  return str;
224 }
225 
226 void MythDB::DBError(const QString &where, const MSqlQuery &query)
227 {
228  LOG(VB_GENERAL, LOG_ERR, GetError(where, query));
229 }
230 
231 QString MythDB::DBErrorMessage(const QSqlError& err)
232 {
233  if (!err.type())
234  return "No error type from QSqlError? Strange...";
235 
236  return QString("Driver error was [%1/%2]:\n"
237  "%3\n"
238  "Database error was:\n"
239  "%4\n")
240  .arg(err.type())
241  .arg(err.nativeErrorCode(),
242  err.driverText(),
243  err.databaseText());
244 }
245 
247 {
248  return d->m_dbParams;
249 }
250 
252 {
253  d->m_dbParams = params;
254 }
255 
256 void MythDB::SetLocalHostname(const QString &name)
257 {
258  if (d->m_localhostname != name.toLower())
259  {
260  d->m_localhostname = name.toLower();
262  }
263 }
264 
265 QString MythDB::GetHostName(void) const
266 {
267  return d->m_localhostname;
268 }
269 
270 void MythDB::IgnoreDatabase(bool bIgnore)
271 {
272  d->m_ignoreDatabase = bIgnore;
273 }
274 
276 {
277  return d->m_ignoreDatabase;
278 }
279 
280 void MythDB::SetSuppressDBMessages(bool bUpgraded)
281 {
282  d->m_suppressDBMessages = bUpgraded;
283 }
284 
286 {
287  return d->m_suppressDBMessages;
288 }
289 
290 void MythDB::SaveSetting(const QString &key, int newValue)
291 {
292  (void) SaveSettingOnHost(key,
293  QString::number(newValue), d->m_localhostname);
294 }
295 
296 void MythDB::SaveSetting(const QString &key, const QString &newValue)
297 {
298  (void) SaveSettingOnHost(key, newValue, d->m_localhostname);
299 }
300 
301 bool MythDB::SaveSettingOnHost(const QString &key,
302  const QString &newValueRaw,
303  const QString &host)
304 {
305  QString loc = QString("SaveSettingOnHost('%1') ").arg(key);
306  if (key.isEmpty())
307  {
308  LOG(VB_GENERAL, LOG_ERR, loc + "- Illegal null key");
309  return false;
310  }
311 
312  QString newValue = (newValueRaw.isNull()) ? "" : newValueRaw;
313 
314  if (d->m_ignoreDatabase)
315  {
316  if (host.toLower() == d->m_localhostname)
317  {
318  if (newValue != kClearSettingValue)
319  OverrideSettingForSession(key, newValue);
320  else
321  ClearOverrideSettingForSession(key);
322  }
323  return true;
324  }
325 
326  if (!HaveValidDatabase()) // Bootstrapping without database?
327  {
328  if (host.toLower() == d->m_localhostname)
329  OverrideSettingForSession(key, newValue);
330  if (!d->m_suppressDBMessages)
331  LOG(VB_GENERAL, LOG_ERR, loc + "- No database yet");
332  SingleSetting setting;
333  setting.m_host = host;
334  setting.m_key = key;
335  setting.m_value = newValue;
336  d->m_delayedSettings.append(setting);
337  return false;
338  }
339 
340  bool success = false;
341 
342  MSqlQuery query(MSqlQuery::InitCon());
343  if (query.isConnected())
344  {
345 
346  if (!host.isEmpty())
347  {
348  query.prepare("DELETE FROM settings WHERE value = :KEY "
349  "AND hostname = :HOSTNAME ;");
350  }
351  else
352  {
353  query.prepare("DELETE FROM settings WHERE value = :KEY "
354  "AND hostname is NULL;");
355  }
356 
357  query.bindValue(":KEY", key);
358  if (!host.isEmpty())
359  query.bindValue(":HOSTNAME", host);
360 
361  if (!query.exec())
362  {
363  if (!GetMythDB()->SuppressDBMessages())
364  MythDB::DBError("Clear setting", query);
365  }
366  else
367  {
368  success = true;
369  }
370  }
371 
372  if (success && (newValue != kClearSettingValue))
373  {
374  if (!host.isEmpty())
375  {
376  query.prepare("INSERT INTO settings (value,data,hostname) "
377  "VALUES ( :VALUE, :DATA, :HOSTNAME );");
378  }
379  else
380  {
381  query.prepare("INSERT INTO settings (value,data ) "
382  "VALUES ( :VALUE, :DATA );");
383  }
384 
385  query.bindValue(":VALUE", key);
386  query.bindValue(":DATA", newValue);
387  if (!host.isEmpty())
388  query.bindValue(":HOSTNAME", host);
389 
390  if (!query.exec())
391  {
392  success = false;
393  if (!(GetMythDB()->SuppressDBMessages()))
394  MythDB::DBError(loc + "- query failure: ", query);
395  }
396  }
397  else if (!success)
398  {
399  LOG(VB_GENERAL, LOG_ERR, loc + "- database not open");
400  }
401 
402  ClearSettingsCache(host + ' ' + key);
403 
404  return success;
405 }
406 
407 bool MythDB::ClearSetting(const QString &key)
408 {
409  return ClearSettingOnHost(key, d->m_localhostname);
410 }
411 
412 bool MythDB::ClearSettingOnHost(const QString &key, const QString &host)
413 {
414  return SaveSettingOnHost(key, kClearSettingValue, host);
415 }
416 
417 QString MythDB::GetSetting(const QString &_key, const QString &defaultval)
418 {
419  QString key = _key.toLower();
420  QString value = defaultval;
421 
422  d->m_settingsCacheLock.lockForRead();
423  if (d->m_useSettingsCache)
424  {
425  SettingsMap::const_iterator it = d->m_settingsCache.constFind(key);
426  if (it != d->m_settingsCache.constEnd())
427  {
428  value = *it;
429  d->m_settingsCacheLock.unlock();
430  return value;
431  }
432  }
433  SettingsMap::const_iterator it = d->m_overriddenSettings.constFind(key);
434  if (it != d->m_overriddenSettings.constEnd())
435  {
436  value = *it;
437  d->m_settingsCacheLock.unlock();
438  return value;
439  }
440  d->m_settingsCacheLock.unlock();
441 
442  if (d->m_ignoreDatabase || !HaveValidDatabase())
443  return value;
444 
445  MSqlQuery query(MSqlQuery::InitCon());
446  if (!query.isConnected())
447  return value;
448 
449  query.prepare(
450  "SELECT data "
451  "FROM settings "
452  "WHERE value = :KEY AND hostname = :HOSTNAME");
453  query.bindValue(":KEY", key);
454  query.bindValue(":HOSTNAME", d->m_localhostname);
455 
456  if (query.exec() && query.next())
457  {
458  value = query.value(0).toString();
459  }
460  else
461  {
462  query.prepare(
463  "SELECT data "
464  "FROM settings "
465  "WHERE value = :KEY AND hostname IS NULL");
466  query.bindValue(":KEY", key);
467 
468  if (query.exec() && query.next())
469  {
470  value = query.value(0).toString();
471  }
472  }
473 
474  if (d->m_useSettingsCache && value != kSentinelValue)
475  {
476  key.squeeze();
477  value.squeeze();
478  d->m_settingsCacheLock.lockForWrite();
479  // another thread may have inserted a value into the cache
480  // while we did not have the lock, check first then save
481  if (d->m_settingsCache.find(key) == d->m_settingsCache.end())
482  d->m_settingsCache[key] = value;
483  d->m_settingsCacheLock.unlock();
484  }
485 
486  return value;
487 }
488 
489 bool MythDB::GetSettings(QMap<QString,QString> &_key_value_pairs)
490 {
491  QMap<QString,bool> done;
492  using KVIt = QMap<QString,QString>::iterator;
493  KVIt kvit = _key_value_pairs.begin();
494  for (; kvit != _key_value_pairs.end(); ++kvit)
495  done[kvit.key().toLower()] = false;
496 
497  QMap<QString,bool>::iterator dit = done.begin();
498  kvit = _key_value_pairs.begin();
499 
500  {
501  uint done_cnt = 0;
502  d->m_settingsCacheLock.lockForRead();
503  if (d->m_useSettingsCache)
504  {
505  for (; kvit != _key_value_pairs.end(); ++dit, ++kvit)
506  {
507  SettingsMap::const_iterator it = d->m_settingsCache.constFind(dit.key());
508  if (it != d->m_settingsCache.constEnd())
509  {
510  *kvit = *it;
511  *dit = true;
512  done_cnt++;
513  }
514  }
515  }
516  for (; kvit != _key_value_pairs.end(); ++dit, ++kvit)
517  {
518  SettingsMap::const_iterator it =
519  d->m_overriddenSettings.constFind(dit.key());
520  if (it != d->m_overriddenSettings.constEnd())
521  {
522  *kvit = *it;
523  *dit = true;
524  done_cnt++;
525  }
526  }
527  d->m_settingsCacheLock.unlock();
528 
529  // Avoid extra work if everything was in the caches and
530  // also don't try to access the DB if m_ignoreDatabase is set
531  if (((uint)done.size()) == done_cnt || d->m_ignoreDatabase)
532  return true;
533  }
534 
535  dit = done.begin();
536  kvit = _key_value_pairs.begin();
537 
538  QString keylist("");
539  QMap<QString,KVIt> keymap;
540  for (; kvit != _key_value_pairs.end(); ++dit, ++kvit)
541  {
542  if (*dit)
543  continue;
544 
545  const QString& key = dit.key();
546  if (!key.contains("'"))
547  {
548  keylist += QString("'%1',").arg(key);
549  keymap[key] = kvit;
550  }
551  else
552  { // hopefully no one actually uses quotes for in a settings key.
553  // but in case they do, just get that value inefficiently..
554  *kvit = GetSetting(key, *kvit);
555  }
556  }
557 
558  if (keylist.isEmpty())
559  return true;
560 
561  keylist = keylist.left(keylist.length() - 1);
562 
563  MSqlQuery query(MSqlQuery::InitCon());
564  if (!query.exec(
565  QString(
566  "SELECT value, data, hostname "
567  "FROM settings "
568  "WHERE (hostname = '%1' OR hostname IS NULL) AND "
569  " value IN (%2) "
570  "ORDER BY hostname DESC")
571  .arg(d->m_localhostname, keylist)))
572  {
573  if (!d->m_suppressDBMessages)
574  DBError("GetSettings", query);
575  return false;
576  }
577 
578  while (query.next())
579  {
580  QString key = query.value(0).toString().toLower();
581  QMap<QString,KVIt>::const_iterator it = keymap.constFind(key);
582  if (it != keymap.constEnd())
583  **it = query.value(1).toString();
584  }
585 
586  if (d->m_useSettingsCache)
587  {
588  d->m_settingsCacheLock.lockForWrite();
589  for (auto it = keymap.cbegin(); it != keymap.cend(); ++it)
590  {
591  QString key = it.key();
592  QString value = **it;
593 
594  // another thread may have inserted a value into the cache
595  // while we did not have the lock, check first then save
596  if (d->m_settingsCache.find(key) == d->m_settingsCache.end())
597  {
598  key.squeeze();
599  value.squeeze();
600  d->m_settingsCache[key] = value;
601  }
602  }
603  d->m_settingsCacheLock.unlock();
604  }
605 
606  return true;
607 }
608 
609 
610 bool MythDB::GetBoolSetting(const QString &key, bool defaultval)
611 {
612  QString val = QString::number(static_cast<int>(defaultval));
613  QString retval = GetSetting(key, val);
614 
615  return retval.toInt() > 0;
616 }
617 
618 int MythDB::GetNumSetting(const QString &key, int defaultval)
619 {
620  QString val = QString::number(defaultval);
621  QString retval = GetSetting(key, val);
622 
623  return retval.toInt();
624 }
625 
626 double MythDB::GetFloatSetting(const QString &key, double defaultval)
627 {
628  QString val = QString::number(defaultval);
629  QString retval = GetSetting(key, val);
630 
631  return retval.toDouble();
632 }
633 
634 QString MythDB::GetSetting(const QString &key)
635 {
636  QString sentinel = QString(kSentinelValue);
637  QString retval = GetSetting(key, sentinel);
638  return (retval == sentinel) ? "" : retval;
639 }
640 
641 bool MythDB::GetBoolSetting(const QString &key)
642 {
643  QString sentinel = QString(kSentinelValue);
644  QString retval = GetSetting(key, sentinel);
645  if (retval == sentinel)
646  return false;
647  return retval.toInt() > 0;
648 }
649 
650 int MythDB::GetNumSetting(const QString &key)
651 {
652  QString sentinel = QString(kSentinelValue);
653  QString retval = GetSetting(key, sentinel);
654  return (retval == sentinel) ? 0 : retval.toInt();
655 }
656 
657 double MythDB::GetFloatSetting(const QString &key)
658 {
659  QString sentinel = QString(kSentinelValue);
660  QString retval = GetSetting(key, sentinel);
661  return (retval == sentinel) ? 0.0 : retval.toDouble();
662 }
663 
664 QString MythDB::GetSettingOnHost(const QString &_key, const QString &_host,
665  const QString &defaultval)
666 {
667  QString key = _key.toLower();
668  QString host = _host.toLower();
669  QString value = defaultval;
670  QString myKey = host + ' ' + key;
671 
672  d->m_settingsCacheLock.lockForRead();
673  if (d->m_useSettingsCache)
674  {
675  SettingsMap::const_iterator it = d->m_settingsCache.constFind(myKey);
676  if (it != d->m_settingsCache.constEnd())
677  {
678  value = *it;
679  d->m_settingsCacheLock.unlock();
680  return value;
681  }
682  }
683  SettingsMap::const_iterator it = d->m_overriddenSettings.constFind(myKey);
684  if (it != d->m_overriddenSettings.constEnd())
685  {
686  value = *it;
687  d->m_settingsCacheLock.unlock();
688  return value;
689  }
690  d->m_settingsCacheLock.unlock();
691 
692  if (d->m_ignoreDatabase)
693  return value;
694 
695  MSqlQuery query(MSqlQuery::InitCon());
696  if (!query.isConnected())
697  {
698  if (!d->m_suppressDBMessages)
699  {
700  LOG(VB_GENERAL, LOG_ERR,
701  QString("Database not open while trying to "
702  "load setting: %1").arg(key));
703  }
704  return value;
705  }
706 
707  query.prepare(
708  "SELECT data "
709  "FROM settings "
710  "WHERE value = :VALUE AND hostname = :HOSTNAME");
711  query.bindValue(":VALUE", key);
712  query.bindValue(":HOSTNAME", host);
713 
714  if (query.exec() && query.next())
715  {
716  value = query.value(0).toString();
717  }
718 
719  if (d->m_useSettingsCache && value != kSentinelValue)
720  {
721  myKey.squeeze();
722  value.squeeze();
723  d->m_settingsCacheLock.lockForWrite();
724  if (d->m_settingsCache.find(myKey) == d->m_settingsCache.end())
725  d->m_settingsCache[myKey] = value;
726  d->m_settingsCacheLock.unlock();
727  }
728 
729  return value;
730 }
731 
732 int MythDB::GetNumSettingOnHost(const QString &key, const QString &host,
733  int defaultval)
734 {
735  QString val = QString::number(defaultval);
736  QString retval = GetSettingOnHost(key, host, val);
737 
738  return retval.toInt();
739 }
740 
742  const QString &key, const QString &host, double defaultval)
743 {
744  QString val = QString::number(defaultval);
745  QString retval = GetSettingOnHost(key, host, val);
746 
747  return retval.toDouble();
748 }
749 
750 QString MythDB::GetSettingOnHost(const QString &key, const QString &host)
751 {
752  QString sentinel = QString(kSentinelValue);
753  QString retval = GetSettingOnHost(key, host, sentinel);
754  return (retval == sentinel) ? "" : retval;
755 }
756 
757 int MythDB::GetNumSettingOnHost(const QString &key, const QString &host)
758 {
759  QString sentinel = QString(kSentinelValue);
760  QString retval = GetSettingOnHost(key, host, sentinel);
761  return (retval == sentinel) ? 0 : retval.toInt();
762 }
763 
764 double MythDB::GetFloatSettingOnHost(const QString &key, const QString &host)
765 {
766  QString sentinel = QString(kSentinelValue);
767  QString retval = GetSettingOnHost(key, host, sentinel);
768  return (retval == sentinel) ? 0.0 : retval.toDouble();
769 }
770 
771 void MythDB::GetResolutionSetting(const QString &type,
772  int &width, int &height,
773  double &forced_aspect,
774  double &refresh_rate,
775  int index)
776 {
777  bool ok = false;
778  bool ok0 = false;
779  bool ok1 = false;
780  QString sRes = QString("%1Resolution").arg(type);
781  QString sRR = QString("%1RefreshRate").arg(type);
782  QString sAspect = QString("%1ForceAspect").arg(type);
783  QString sWidth = QString("%1Width").arg(type);
784  QString sHeight = QString("%1Height").arg(type);
785  if (index >= 0)
786  {
787  sRes = QString("%1Resolution%2").arg(type).arg(index);
788  sRR = QString("%1RefreshRate%2").arg(type).arg(index);
789  sAspect = QString("%1ForceAspect%2").arg(type).arg(index);
790  sWidth = QString("%1Width%2").arg(type).arg(index);
791  sHeight = QString("%1Height%2").arg(type).arg(index);
792  }
793 
794  QString res = GetSetting(sRes);
795 
796  if (!res.isEmpty())
797  {
798  QStringList slist = res.split(QString("x"));
799  int w = width;
800  int h = height;
801  if (2 == slist.size())
802  {
803  w = slist[0].toInt(&ok0);
804  h = slist[1].toInt(&ok1);
805  }
806  ok = ok0 && ok1;
807  if (ok)
808  {
809  width = w;
810  height = h;
811  refresh_rate = GetFloatSetting(sRR);
812  forced_aspect = GetFloatSetting(sAspect);
813  }
814  }
815 
816  if (!ok)
817  {
818  int tmpWidth = GetNumSetting(sWidth, width);
819  if (tmpWidth)
820  width = tmpWidth;
821 
822  int tmpHeight = GetNumSetting(sHeight, height);
823  if (tmpHeight)
824  height = tmpHeight;
825 
826  refresh_rate = 0.0;
827  forced_aspect = 0.0;
828  //SetSetting(sRes, QString("%1x%2").arg(width).arg(height));
829  }
830 }
831 
832 void MythDB::GetResolutionSetting(const QString &t, int &w, int &h, int i)
833 {
834  double forced_aspect = 0;
835  double refresh_rate = 0.0;
836  GetResolutionSetting(t, w, h, forced_aspect, refresh_rate, i);
837 }
838 
839 
847  const QString &key, const QString &value)
848 {
849  QString mk = key.toLower();
850  QString mk2 = d->m_localhostname + ' ' + mk;
851  QString mv = value;
852  if ("dbschemaver" == mk)
853  {
854  LOG(VB_GENERAL, LOG_ERR,
855  QString("ERROR: Refusing to allow override for '%1'.").arg(key));
856  return;
857  }
858  mk.squeeze();
859  mk2.squeeze();
860  mv.squeeze();
861 
862  d->m_settingsCacheLock.lockForWrite();
863  d->m_overriddenSettings[mk] = mv;
864  d->m_settingsCache[mk] = mv;
865  d->m_settingsCache[mk2] = mv;
866  d->m_settingsCacheLock.unlock();
867 }
868 
871 {
872  QString mk = key.toLower();
873  QString mk2 = d->m_localhostname + ' ' + mk;
874 
875  d->m_settingsCacheLock.lockForWrite();
876 
877  SettingsMap::iterator oit = d->m_overriddenSettings.find(mk);
878  if (oit != d->m_overriddenSettings.end())
879  d->m_overriddenSettings.erase(oit);
880 
881  SettingsMap::iterator sit = d->m_settingsCache.find(mk);
882  if (sit != d->m_settingsCache.end())
883  d->m_settingsCache.erase(sit);
884 
885  sit = d->m_settingsCache.find(mk2);
886  if (sit != d->m_settingsCache.end())
887  d->m_settingsCache.erase(sit);
888 
889  d->m_settingsCacheLock.unlock();
890 }
891 
892 static void clear(
893  SettingsMap &cache, SettingsMap &overrides, const QString &myKey)
894 {
895  // Do the actual clearing..
896  SettingsMap::iterator it = cache.find(myKey);
897  if (it != cache.end())
898  {
899  SettingsMap::const_iterator oit = overrides.constFind(myKey);
900  if (oit == overrides.constEnd())
901  {
902  LOG(VB_DATABASE, LOG_INFO,
903  QString("Clearing Settings Cache for '%1'.").arg(myKey));
904  cache.erase(it);
905  }
906  else
907  {
908  LOG(VB_DATABASE, LOG_INFO,
909  QString("Clearing Cache of overridden '%1' ignored.")
910  .arg(myKey));
911  }
912  }
913 }
914 
915 void MythDB::ClearSettingsCache(const QString &_key)
916 {
917  d->m_settingsCacheLock.lockForWrite();
918 
919  if (_key.isEmpty())
920  {
921  LOG(VB_DATABASE, LOG_INFO, "Clearing Settings Cache.");
922  d->m_settingsCache.clear();
923  d->m_settingsCache.reserve(settings_reserve);
924 
925  SettingsMap::const_iterator it = d->m_overriddenSettings.cbegin();
926  for (; it != d->m_overriddenSettings.cend(); ++it)
927  {
928  QString mk2 = d->m_localhostname + ' ' + it.key();
929  mk2.squeeze();
930 
931  d->m_settingsCache[it.key()] = *it;
932  d->m_settingsCache[mk2] = *it;
933  }
934  }
935  else
936  {
937  QString myKey = _key.toLower();
938  clear(d->m_settingsCache, d->m_overriddenSettings, myKey);
939 
940  // To be safe always clear any local[ized] version too
941  QString mkl = myKey.section(QChar(' '), 1);
942  if (!mkl.isEmpty())
943  clear(d->m_settingsCache, d->m_overriddenSettings, mkl);
944  }
945 
946  d->m_settingsCacheLock.unlock();
947 }
948 
949 void MythDB::ActivateSettingsCache(bool activate)
950 {
951  if (activate)
952  LOG(VB_DATABASE, LOG_INFO, "Enabling Settings Cache.");
953  else
954  LOG(VB_DATABASE, LOG_INFO, "Disabling Settings Cache.");
955 
956  d->m_useSettingsCache = activate;
958 }
959 
961 {
962  if (!HaveValidDatabase())
963  return;
964 
965  if (!gCoreContext->IsUIThread())
966  return;
967 
968  while (!d->m_delayedSettings.isEmpty())
969  {
970  SingleSetting setting = d->m_delayedSettings.takeFirst();
971  SaveSettingOnHost(setting.m_key, setting.m_value, setting.m_host);
972  }
973 }
974 
978 void MythDB::SetHaveDBConnection(bool connected)
979 {
980  d->m_haveDBConnection = connected;
981 }
982 
987 void MythDB::SetHaveSchema(bool schema)
988 {
989  d->m_haveSchema = schema;
990 }
991 
999 bool MythDB::HaveSchema(void) const
1000 {
1001  return d->m_haveSchema;
1002 }
1003 
1012 {
1013  return (d->m_haveDBConnection && d->m_haveSchema);
1014 }
MythDB::GetBoolSetting
bool GetBoolSetting(const QString &key, bool defaultval)
Definition: mythdb.cpp:610
MythDBPrivate::m_haveSchema
bool m_haveSchema
Definition: mythdb.cpp:115
MSqlQuery::next
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:806
MSqlQuery
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
DestroyMythDB
void DestroyMythDB(void)
Definition: mythdb.cpp:54
MythDB::WriteDelayedSettings
void WriteDelayedSettings(void)
Definition: mythdb.cpp:960
dbLock
static QMutex dbLock
Definition: mythdb.cpp:22
MythDB::HaveSchema
bool HaveSchema(void) const
Get a flag indicating that we have discovered tables and that this therefore not a new empty database...
Definition: mythdb.cpp:999
DatabaseParams::m_dbHostName
QString m_dbHostName
database server
Definition: mythdbparams.h:22
MythDB::IsDatabaseIgnored
bool IsDatabaseIgnored(void) const
Definition: mythdb.cpp:275
MythDB::IgnoreDatabase
void IgnoreDatabase(bool bIgnore)
Definition: mythdb.cpp:270
MythDB::~MythDB
~MythDB()
Definition: mythdb.cpp:136
MythDB::GetSettingOnHost
QString GetSettingOnHost(const QString &_key, const QString &_host, const QString &defaultval)
Definition: mythdb.cpp:664
mythdb.h
MythDB::DBErrorMessage
static QString DBErrorMessage(const QSqlError &err)
Definition: mythdb.cpp:231
MythDB::ClearSettingOnHost
bool ClearSettingOnHost(const QString &key, const QString &host)
Definition: mythdb.cpp:412
kClearSettingValue
const char *const kClearSettingValue
Definition: mythdb.cpp:26
DatabaseParams
Structure containing the basic Database parameters.
Definition: mythdbparams.h:10
MythDB::ActivateSettingsCache
void ActivateSettingsCache(bool activate=true)
Definition: mythdb.cpp:949
MDBManager
DB connection pool, used by MSqlQuery. Do not use directly.
Definition: mythdbcon.h:53
MythDBPrivate::m_settingsCache
SettingsMap m_settingsCache
Permanent settings in the DB and overridden settings.
Definition: mythdb.cpp:107
SingleSetting
Definition: mythdb.cpp:82
MSqlQuery::value
QVariant value(int i) const
Definition: mythdbcon.h:202
mythdbcon.h
MSqlQuery::exec
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:607
MythCoreContext::IsUIThread
bool IsUIThread(void)
Definition: mythcorecontext.cpp:1372
MythDBPrivate::m_localhostname
QString m_localhostname
Definition: mythdb.cpp:98
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
MythDBPrivate::m_dbParams
DatabaseParams m_dbParams
Current database host & WOL details.
Definition: mythdb.cpp:97
GetMythDB
MythDB * GetMythDB(void)
Definition: mythdb.cpp:49
mythdirs.h
MythDB::GetFloatSettingOnHost
double GetFloatSettingOnHost(const QString &key, const QString &host, double defaultval)
Definition: mythdb.cpp:741
MythDBPrivate::m_useSettingsCache
volatile bool m_useSettingsCache
Definition: mythdb.cpp:105
MythDBPrivate::m_dbmanager
MDBManager m_dbmanager
Definition: mythdb.cpp:99
MythDBPrivate
Definition: mythdb.cpp:91
tmp
static guint32 * tmp
Definition: goom_core.cpp:31
MythDB::GetFloatSetting
double GetFloatSetting(const QString &key, double defaultval)
Definition: mythdb.cpp:626
MythDBPrivate::m_suppressDBMessages
bool m_suppressDBMessages
Definition: mythdb.cpp:102
MythDB::ClearSettingsCache
void ClearSettingsCache(const QString &key=QString())
Definition: mythdb.cpp:915
MythDB::destroyMythDB
static void destroyMythDB()
Definition: mythdb.cpp:41
MythDBPrivate::m_settingsCacheLock
QReadWriteLock m_settingsCacheLock
Definition: mythdb.cpp:104
MythDB::SaveSettingOnHost
bool SaveSettingOnHost(const QString &key, const QString &newValue, const QString &host)
Definition: mythdb.cpp:301
SingleSetting::m_host
QString m_host
Definition: mythdb.cpp:86
MythDB::GetDatabaseParams
DatabaseParams GetDatabaseParams(void) const
Definition: mythdb.cpp:246
MythDB::ClearSetting
bool ClearSetting(const QString &key)
Definition: mythdb.cpp:407
mythlogging.h
MythDBPrivate::m_overriddenSettings
SettingsMap m_overriddenSettings
Overridden this session only.
Definition: mythdb.cpp:109
MythDBPrivate::MythDBPrivate
MythDBPrivate()
Definition: mythdb.cpp:120
mythdb
static MythDB * mythdb
Definition: mythdb.cpp:21
MythDB::GetNumSetting
int GetNumSetting(const QString &key, int defaultval)
Definition: mythdb.cpp:618
MythDB::GetError
static QString GetError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:195
hardwareprofile.i18n.t
t
Definition: i18n.py:36
MSqlQuery::InitCon
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:539
MythDB::DBError
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:226
MythDB::SuppressDBMessages
bool SuppressDBMessages(void) const
Definition: mythdb.cpp:285
MythDBPrivate::m_ignoreDatabase
bool m_ignoreDatabase
Definition: mythdb.cpp:101
MythDB::GetNumSettingOnHost
int GetNumSettingOnHost(const QString &key, const QString &host, int defaultval)
Definition: mythdb.cpp:732
MSqlQuery::boundValues
QVariantList boundValues(void) const
Definition: mythdbcon.h:209
SingleSetting::m_value
QString m_value
Definition: mythdb.cpp:85
clear
static void clear(SettingsMap &cache, SettingsMap &overrides, const QString &myKey)
Definition: mythdb.cpp:892
kSentinelValue
const char *const kSentinelValue
Definition: mythdb.cpp:25
SingleSetting::m_key
QString m_key
Definition: mythdb.cpp:84
MythDBPrivate::~MythDBPrivate
~MythDBPrivate()
Definition: mythdb.cpp:126
MSqlQuery::isConnected
bool isConnected(void) const
Only updated once during object creation.
Definition: mythdbcon.h:135
uint
unsigned int uint
Definition: compat.h:144
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:60
SettingsMap
QHash< QString, QString > SettingsMap
Definition: mythdb.cpp:89
MythDB::getMythDB
static MythDB * getMythDB()
Definition: mythdb.cpp:28
MythDB::SetHaveSchema
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:987
MSqlQuery::lastError
QSqlError lastError(void) const
Definition: mythdbcon.h:211
MythDB::SetLocalHostname
void SetLocalHostname(const QString &name)
Definition: mythdb.cpp:256
MythDB::GetResolutionSetting
void GetResolutionSetting(const QString &type, int &width, int &height, double &forced_aspect, double &refresh_rate, int index=-1)
MythRandom
MBASE_PUBLIC uint32_t MythRandom()
Definition: mythmiscutil.h:25
MythDB::GetHostName
QString GetHostName(void) const
Definition: mythdb.cpp:265
MythDB::MythDB
MythDB()
Definition: mythdb.cpp:131
mythmiscutil.h
MythDBPrivate::m_delayedSettings
QList< SingleSetting > m_delayedSettings
Settings which should be written to the database as soon as it becomes available.
Definition: mythdb.cpp:112
mythcorecontext.h
MSqlQuery::executedQuery
QString executedQuery(void) const
Definition: mythdbcon.h:203
GetMythTestDB
MythDB * GetMythTestDB(const QString &testname)
Definition: mythdb.cpp:59
toCommaList
static QString toCommaList(const QSet< uint > &list)
Definition: tv_play.cpp:6681
MythDB::GetSetting
QString GetSetting(const QString &_key, const QString &defaultval)
Definition: mythdb.cpp:417
MythDB::SetDatabaseParams
void SetDatabaseParams(const DatabaseParams &params)
Definition: mythdb.cpp:251
MythDB::SetSuppressDBMessages
void SetSuppressDBMessages(bool bUpgraded)
Definition: mythdb.cpp:280
MSqlQuery::bindValue
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:882
MythDBPrivate::m_haveDBConnection
bool m_haveDBConnection
Definition: mythdb.cpp:114
MythDB::GetSettings
bool GetSettings(QMap< QString, QString > &_key_value_pairs)
Definition: mythdb.cpp:489
ClearSettingsCache
static int ClearSettingsCache(const MythUtilCommandLineParser &)
Definition: backendutils.cpp:39
MythDB::SaveSetting
void SaveSetting(const QString &key, int newValue)
Definition: mythdb.cpp:290
MythDB::SetHaveDBConnection
void SetHaveDBConnection(bool connected)
Set a flag indicating we have successfully connected to the database.
Definition: mythdb.cpp:978
MythDB::HaveValidDatabase
bool HaveValidDatabase(void) const
Returns true if we have successfully connected to the database and that database has tables.
Definition: mythdb.cpp:1011
settings_reserve
static const int settings_reserve
Definition: mythdb.cpp:118
MythDB::OverrideSettingForSession
void OverrideSettingForSession(const QString &key, const QString &newValue)
Overrides the given setting for the execution time of the process.
Definition: mythdb.cpp:846
d
static const iso6937table * d
Definition: iso6937tables.cpp:1025
indent
static QString indent(uint level)
Definition: mythsettings.cpp:17
MythDB::GetDBManager
MDBManager * GetDBManager(void)
Definition: mythdb.cpp:141
MythDB::ClearOverrideSettingForSession
void ClearOverrideSettingForSession(const QString &key)
Clears session Overrides for the given setting.
Definition: mythdb.cpp:870
MythDB::toCommaList
static QString toCommaList(const QMap< QString, QVariant > &bindings, uint indent=0, uint softMaxColumn=80)
Definition: mythdb.cpp:146
MSqlQuery::prepare
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:831