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