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
23static MythDB *mythdb = nullptr;
24static QMutex dbLock;
25
26// For thread safety reasons this is not a QString
27const char * const kSentinelValue { "<settings_sentinel_value>" };
28const char * const kClearSettingValue { "<clear_setting_value>" };
29
30MythDB *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
51MythDB *GetMythDB(void)
52{
53 return MythDB::getMythDB();
54}
55
56void DestroyMythDB(void)
57{
59}
60
61MythDB *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
90using SettingsMap = QHash<QString,QString>;
91
93{
94 public:
97
101
102 bool m_ignoreDatabase {false};
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
119static 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
146QString 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
195QString 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
226void MythDB::DBError(const QString &where, const MSqlQuery &query)
227{
228 LOG(VB_GENERAL, LOG_ERR, GetError(where, query));
229}
230
231QString 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
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
313void MythDB::SetLocalHostname(const QString &name)
314{
315 if (d->m_localhostname != name.toLower())
316 {
317 d->m_localhostname = name.toLower();
319 }
320}
321
322QString MythDB::GetHostName(void) const
323{
324 return d->m_localhostname;
325}
326
327void MythDB::IgnoreDatabase(bool bIgnore)
328{
329 d->m_ignoreDatabase = bIgnore;
330}
331
333{
334 return d->m_ignoreDatabase;
335}
336
338{
339 d->m_suppressDBMessages = bUpgraded;
340}
341
343{
344 return d->m_suppressDBMessages;
345}
346
347void MythDB::SaveSetting(const QString &key, int newValue)
348{
349 (void) SaveSettingOnHost(key,
350 QString::number(newValue), d->m_localhostname);
351}
352
353void MythDB::SaveSetting(const QString &key, const QString &newValue)
354{
355 (void) SaveSettingOnHost(key, newValue, d->m_localhostname);
356}
357
358bool 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
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
464bool MythDB::ClearSetting(const QString &key)
465{
466 return ClearSettingOnHost(key, d->m_localhostname);
467}
468
469bool MythDB::ClearSettingOnHost(const QString &key, const QString &host)
470{
471 return SaveSettingOnHost(key, kClearSettingValue, host);
472}
473
474QString 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
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.contains(key))
539 d->m_settingsCache[key] = value;
540 d->m_settingsCacheLock.unlock();
541 }
542
543 return value;
544}
545
546bool 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
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.contains(key))
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
667bool 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
675int 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
683double 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
691QString MythDB::GetSetting(const QString &key)
692{
693 QString sentinel = QString(kSentinelValue);
694 QString retval = GetSetting(key, sentinel);
695 return (retval == sentinel) ? "" : retval;
696}
697
698bool 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
707int 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
714double 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
721QString 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
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.contains(myKey))
782 d->m_settingsCache[myKey] = value;
783 d->m_settingsCacheLock.unlock();
784 }
785
786 return value;
787}
788
789int 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
807QString 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
814int 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
821double 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
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
889void 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
949static 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
972void 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
1036{
1037 d->m_haveDBConnection = connected;
1038}
1039
1044void MythDB::SetHaveSchema(bool schema)
1045{
1046 d->m_haveSchema = schema;
1047}
1048
1056bool MythDB::HaveSchema(void) const
1057{
1058 return d->m_haveSchema;
1059}
1060
1069{
1070 return (d->m_haveDBConnection && d->m_haveSchema);
1071}
static int ClearSettingsCache(const MythUtilCommandLineParser &)
Structure containing the basic Database parameters.
Definition: mythdbparams.h:11
QString m_dbName
database name
Definition: mythdbparams.h:26
QString m_dbPassword
DB password.
Definition: mythdbparams.h:25
std::chrono::seconds m_wolReconnect
seconds to wait for reconnect
Definition: mythdbparams.h:34
QString m_localHostName
name used for loading/saving settings
Definition: mythdbparams.h:30
bool m_dbHostPing
No longer used.
Definition: mythdbparams.h:22
QString m_dbUserName
DB user name.
Definition: mythdbparams.h:24
QString m_wolCommand
command to use for wake-on-lan
Definition: mythdbparams.h:36
bool m_wolEnabled
true if wake-on-lan params are used
Definition: mythdbparams.h:33
int m_dbPort
database port
Definition: mythdbparams.h:23
int m_wolRetry
times to retry to reconnect
Definition: mythdbparams.h:35
QString m_dbHostName
database server
Definition: mythdbparams.h:21
DB connection pool, used by MSqlQuery. Do not use directly.
Definition: mythdbcon.h:55
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:128
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:837
QString executedQuery(void) const
Definition: mythdbcon.h:205
QSqlError lastError(void) const
Definition: mythdbcon.h:213
QVariant value(int i) const
Definition: mythdbcon.h:204
bool isConnected(void) const
Only updated once during object creation.
Definition: mythdbcon.h:137
QVariantList boundValues(void) const
Definition: mythdbcon.h:211
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:618
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:888
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:812
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:550
QReadWriteLock m_settingsCacheLock
Definition: mythdb.cpp:105
bool m_haveSchema
Definition: mythdb.cpp:116
bool m_haveDBConnection
Definition: mythdb.cpp:115
QString m_localhostname
Definition: mythdb.cpp:99
MDBManager m_dbmanager
Definition: mythdb.cpp:100
bool m_suppressDBMessages
Definition: mythdb.cpp:103
volatile bool m_useSettingsCache
Definition: mythdb.cpp:106
bool m_ignoreDatabase
Definition: mythdb.cpp:102
SettingsMap m_settingsCache
Permanent settings in the DB and overridden settings.
Definition: mythdb.cpp:108
SettingsMap m_overriddenSettings
Overridden this session only.
Definition: mythdb.cpp:110
QList< SingleSetting > m_delayedSettings
Settings which should be written to the database as soon as it becomes available.
Definition: mythdb.cpp:113
DatabaseParams m_dbParams
Current database host & WOL details.
Definition: mythdb.cpp:98
void SetSuppressDBMessages(bool bUpgraded)
Definition: mythdb.cpp:337
QString GetSetting(const QString &_key, const QString &defaultval)
Definition: mythdb.cpp:474
void ActivateSettingsCache(bool activate=true)
Definition: mythdb.cpp:1006
QString GetSettingOnHost(const QString &_key, const QString &_host, const QString &defaultval)
Definition: mythdb.cpp:721
bool GetBoolSetting(const QString &key, bool defaultval)
Definition: mythdb.cpp:667
void SaveSetting(const QString &key, int newValue)
Definition: mythdb.cpp:347
QString GetHostName(void) const
Definition: mythdb.cpp:322
void OverrideSettingForSession(const QString &key, const QString &newValue)
Overrides the given setting for the execution time of the process.
Definition: mythdb.cpp:903
static MythDB * getMythDB()
Definition: mythdb.cpp:30
void IgnoreDatabase(bool bIgnore)
Definition: mythdb.cpp:327
int GetNumSettingOnHost(const QString &key, const QString &host, int defaultval)
Definition: mythdb.cpp:789
bool ClearSetting(const QString &key)
Definition: mythdb.cpp:464
double GetFloatSettingOnHost(const QString &key, const QString &host, double defaultval)
Definition: mythdb.cpp:798
static QString DBErrorMessage(const QSqlError &err)
Definition: mythdb.cpp:231
static QString GetError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:195
static void destroyMythDB()
Definition: mythdb.cpp:43
bool GetSettings(QMap< QString, QString > &_key_value_pairs)
Definition: mythdb.cpp:546
static QString toCommaList(const QMap< QString, QVariant > &bindings, uint indent=0, uint softMaxColumn=80)
Definition: mythdb.cpp:146
~MythDB()
Definition: mythdb.cpp:136
void SetDatabaseParams(const DatabaseParams &params)
Definition: mythdb.cpp:256
bool IsDatabaseIgnored(void) const
Definition: mythdb.cpp:332
void SetHaveDBConnection(bool connected)
Set a flag indicating we have successfully connected to the database.
Definition: mythdb.cpp:1035
bool HaveValidDatabase(void) const
Returns true if we have successfully connected to the database and that database has tables.
Definition: mythdb.cpp:1068
DatabaseParams GetDatabaseParams(void) const
Definition: mythdb.cpp:251
void WriteDelayedSettings(void)
Definition: mythdb.cpp:1017
MythDB()
Definition: mythdb.cpp:132
MDBManager * GetDBManager(void)
Definition: mythdb.cpp:141
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:226
void SetLocalHostname(const QString &name)
Definition: mythdb.cpp:313
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
void ClearSettingsCache(const QString &key=QString())
Definition: mythdb.cpp:972
bool SuppressDBMessages(void) const
Definition: mythdb.cpp:342
double GetFloatSetting(const QString &key, double defaultval)
Definition: mythdb.cpp:683
bool ClearSettingOnHost(const QString &key, const QString &host)
Definition: mythdb.cpp:469
bool SaveSettingOnHost(const QString &key, const QString &newValue, const QString &host)
Definition: mythdb.cpp:358
void GetResolutionSetting(const QString &type, int &width, int &height, double &forced_aspect, double &refresh_rate, int index=-1)
Definition: mythdb.cpp:828
QString GetDatabaseName() const
Definition: mythdb.cpp:246
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
void ClearOverrideSettingForSession(const QString &key)
Clears session Overrides for the given setting.
Definition: mythdb.cpp:927
int GetNumSetting(const QString &key, int defaultval)
Definition: mythdb.cpp:675
bool SaveDatabaseParams(const DatabaseParams &params, bool force)
Definition: mythdb.cpp:261
static const QString kDefaultWOL
Definition: configuration.h:60
static const QString kDefaultDB
Definition: configuration.h:59
unsigned int uint
Definition: compat.h:68
static guint32 * tmp
Definition: goom_core.cpp:26
static const iso6937table * d
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
MythDB * GetMythTestDB(const QString &testname)
Definition: mythdb.cpp:61
static void clear(SettingsMap &cache, SettingsMap &overrides, const QString &myKey)
Definition: mythdb.cpp:949
static QMutex dbLock
Definition: mythdb.cpp:24
QHash< QString, QString > SettingsMap
Definition: mythdb.cpp:90
static const int settings_reserve
Definition: mythdb.cpp:119
MythDB * GetMythDB(void)
Definition: mythdb.cpp:51
const char *const kClearSettingValue
Definition: mythdb.cpp:28
static MythDB * mythdb
Definition: mythdb.cpp:23
const char *const kSentinelValue
Definition: mythdb.cpp:27
void DestroyMythDB(void)
Definition: mythdb.cpp:56
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
Convenience inline random number generator functions.
static QString indent(uint level)
uint32_t MythRandom()
generate 32 random bits
Definition: mythrandom.h:20
QString m_value
Definition: mythdb.cpp:86
QString m_key
Definition: mythdb.cpp:85
QString m_host
Definition: mythdb.cpp:87
static QString toCommaList(const QVector< uint > &list)
Definition: tv_play.cpp:6787