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