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