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