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