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