5#include <QReadWriteLock>
12#include <QRegularExpression>
13#include <QStandardPaths>
66 params.m_dbHostPing =
false;
69 QStandardPaths::writableLocation(QStandardPaths::TempLocation) +
71 QString(
"mythtv_%1.%2.sqlite3")
72 .arg(testname).arg(
MythRandom(),8,16,QLatin1Char(
'0'));
74 params.m_dbName =
":memory:";
76 params.m_dbType =
"QSQLITE";
77 db->SetDatabaseParams(params);
128 LOG(VB_DATABASE, LOG_INFO,
"Destroying MythDBPrivate");
142 return &(
d->m_dbmanager);
148 QMap<QString, QVariant>::const_iterator it = bindings.begin();
149 if (it == bindings.end())
153 QString str = QString(
"%1").arg(
"",
indent);
154 for (; it != bindings.end(); ++it)
156 QString val = (*it).toString();
162#
if QT_VERSION < QT_VERSION_CHECK(6,0,0)
163 it->type() == QVariant::String
165 it->typeId() == QMetaType::QString
169 val = (it->toString().isNull()) ?
170 "NULL" : QString(
"\"%1\"").arg(val);
172 const QString curBinding = it.key() +
'=' + val +
',';
173 if ((curColumn >
indent) &&
174 ((curBinding.length() + curColumn) > maxColumn))
177 str += QString(
"%1").arg(
"",
indent);
186 curColumn += curBinding.length();
188 str = str.left(str.length() - 1);
196 QString str = QString(
"DB Error (%1):\n").arg(where);
198 str +=
"Query was:\n";
200#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
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())
209 auto match = iter.next();
210 namedBindings[match.captured()] = numberedBindings.isEmpty()
212 : numberedBindings.takeFirst();
218 str +=
"Bindings were:\n";
221 str += DBErrorMessage(query.
lastError());
227 LOG(VB_GENERAL, LOG_ERR, GetError(where, query));
233 return "No error type from QSqlError? Strange...";
235 return QString(
"Driver error was [%1/%2]:\n"
237 "Database error was:\n"
240 .arg(err.nativeErrorCode(),
247 return d->m_dbParams.m_dbName;
252 return d->m_dbParams;
257 d->m_dbParams = params;
265 if (
force || (params !=
d->m_dbParams))
282 if (addr.setAddress(dbHostName))
284 addr.setScopeId(QString());
285 dbHostName = addr.toString();
300 success = config.Save();
303 d->m_dbParams = params;
306 GetDBManager()->CloseDatabases();
314 if (
d->m_localhostname != name.toLower())
316 d->m_localhostname = name.toLower();
323 return d->m_localhostname;
328 d->m_ignoreDatabase = bIgnore;
333 return d->m_ignoreDatabase;
338 d->m_suppressDBMessages = bUpgraded;
343 return d->m_suppressDBMessages;
348 (void) SaveSettingOnHost(key,
349 QString::number(newValue),
d->m_localhostname);
354 (void) SaveSettingOnHost(key, newValue,
d->m_localhostname);
358 const QString &newValueRaw,
361 QString loc = QString(
"SaveSettingOnHost('%1') ").arg(key);
364 LOG(VB_GENERAL, LOG_ERR, loc +
"- Illegal null key");
368 QString newValue = (newValueRaw.isNull()) ?
"" : newValueRaw;
370 if (
d->m_ignoreDatabase)
372 if (host.toLower() ==
d->m_localhostname)
375 OverrideSettingForSession(key, newValue);
377 ClearOverrideSettingForSession(key);
382 if (!HaveValidDatabase())
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");
392 d->m_delayedSettings.append(setting);
396 bool success =
false;
404 query.
prepare(
"DELETE FROM settings WHERE value = :KEY "
405 "AND hostname = :HOSTNAME ;");
409 query.
prepare(
"DELETE FROM settings WHERE value = :KEY "
410 "AND hostname is NULL;");
432 query.
prepare(
"INSERT INTO settings (value,data,hostname) "
433 "VALUES ( :VALUE, :DATA, :HOSTNAME );");
437 query.
prepare(
"INSERT INTO settings (value,data ) "
438 "VALUES ( :VALUE, :DATA );");
449 if (!(
GetMythDB()->SuppressDBMessages()))
455 LOG(VB_GENERAL, LOG_ERR, loc +
"- database not open");
465 return ClearSettingOnHost(key,
d->m_localhostname);
475 QString key = _key.toLower();
476 QString value = defaultval;
478 d->m_settingsCacheLock.lockForRead();
479 if (
d->m_useSettingsCache)
481 SettingsMap::const_iterator it =
d->m_settingsCache.constFind(key);
482 if (it !=
d->m_settingsCache.constEnd())
485 d->m_settingsCacheLock.unlock();
489 SettingsMap::const_iterator it =
d->m_overriddenSettings.constFind(key);
490 if (it !=
d->m_overriddenSettings.constEnd())
493 d->m_settingsCacheLock.unlock();
496 d->m_settingsCacheLock.unlock();
498 if (
d->m_ignoreDatabase || !HaveValidDatabase())
508 "WHERE value = :KEY AND hostname = :HOSTNAME");
510 query.
bindValue(
":HOSTNAME",
d->m_localhostname);
514 value = query.
value(0).toString();
521 "WHERE value = :KEY AND hostname IS NULL");
526 value = query.
value(0).toString();
534 d->m_settingsCacheLock.lockForWrite();
537 if (!
d->m_settingsCache.contains(key))
538 d->m_settingsCache[key] = value;
539 d->m_settingsCacheLock.unlock();
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;
553 QMap<QString,bool>::iterator dit = done.begin();
554 kvit = _key_value_pairs.begin();
558 d->m_settingsCacheLock.lockForRead();
559 if (
d->m_useSettingsCache)
561 for (; kvit != _key_value_pairs.end(); ++dit, ++kvit)
563 SettingsMap::const_iterator it =
d->m_settingsCache.constFind(dit.key());
564 if (it !=
d->m_settingsCache.constEnd())
572 for (; kvit != _key_value_pairs.end(); ++dit, ++kvit)
574 SettingsMap::const_iterator it =
575 d->m_overriddenSettings.constFind(dit.key());
576 if (it !=
d->m_overriddenSettings.constEnd())
583 d->m_settingsCacheLock.unlock();
587 if (((
uint)done.size()) == done_cnt ||
d->m_ignoreDatabase)
592 kvit = _key_value_pairs.begin();
595 QMap<QString,KVIt> keymap;
596 for (; kvit != _key_value_pairs.end(); ++dit, ++kvit)
601 const QString& key = dit.key();
602 if (!key.contains(
"'"))
604 keylist += QString(
"'%1',").arg(key);
610 *kvit = GetSetting(key, *kvit);
614 if (keylist.isEmpty())
617 keylist = keylist.left(keylist.length() - 1);
622 "SELECT value, data, hostname "
624 "WHERE (hostname = '%1' OR hostname IS NULL) AND "
626 "ORDER BY hostname DESC")
627 .arg(
d->m_localhostname, keylist)))
629 if (!
d->m_suppressDBMessages)
630 DBError(
"GetSettings", query);
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();
642 if (
d->m_useSettingsCache)
644 d->m_settingsCacheLock.lockForWrite();
645 for (
auto it = keymap.cbegin(); it != keymap.cend(); ++it)
647 QString key = it.key();
648 QString value = **it;
652 if (!
d->m_settingsCache.contains(key))
656 d->m_settingsCache[key] = value;
659 d->m_settingsCacheLock.unlock();
668 QString val = QString::number(
static_cast<int>(defaultval));
669 QString retval = GetSetting(key, val);
671 return retval.toInt() > 0;
676 QString val = QString::number(defaultval);
677 QString retval = GetSetting(key, val);
679 return retval.toInt();
684 QString val = QString::number(defaultval);
685 QString retval = GetSetting(key, val);
687 return retval.toDouble();
693 QString retval = GetSetting(key, sentinel);
694 return (retval == sentinel) ?
"" : retval;
700 QString retval = GetSetting(key, sentinel);
701 if (retval == sentinel)
703 return retval.toInt() > 0;
709 QString retval = GetSetting(key, sentinel);
710 return (retval == sentinel) ? 0 : retval.toInt();
716 QString retval = GetSetting(key, sentinel);
717 return (retval == sentinel) ? 0.0 : retval.toDouble();
721 const QString &defaultval)
723 QString key = _key.toLower();
724 QString host = _host.toLower();
725 QString value = defaultval;
726 QString myKey = host +
' ' + key;
728 d->m_settingsCacheLock.lockForRead();
729 if (
d->m_useSettingsCache)
731 SettingsMap::const_iterator it =
d->m_settingsCache.constFind(myKey);
732 if (it !=
d->m_settingsCache.constEnd())
735 d->m_settingsCacheLock.unlock();
739 SettingsMap::const_iterator it =
d->m_overriddenSettings.constFind(myKey);
740 if (it !=
d->m_overriddenSettings.constEnd())
743 d->m_settingsCacheLock.unlock();
746 d->m_settingsCacheLock.unlock();
748 if (
d->m_ignoreDatabase)
754 if (!
d->m_suppressDBMessages)
756 LOG(VB_GENERAL, LOG_ERR,
757 QString(
"Database not open while trying to "
758 "load setting: %1").arg(key));
766 "WHERE value = :VALUE AND hostname = :HOSTNAME");
772 value = query.
value(0).toString();
779 d->m_settingsCacheLock.lockForWrite();
780 if (!
d->m_settingsCache.contains(myKey))
781 d->m_settingsCache[myKey] = value;
782 d->m_settingsCacheLock.unlock();
791 QString val = QString::number(defaultval);
792 QString retval = GetSettingOnHost(key, host, val);
794 return retval.toInt();
798 const QString &key,
const QString &host,
double defaultval)
800 QString val = QString::number(defaultval);
801 QString retval = GetSettingOnHost(key, host, val);
803 return retval.toDouble();
809 QString retval = GetSettingOnHost(key, host, sentinel);
810 return (retval == sentinel) ?
"" : retval;
816 QString retval = GetSettingOnHost(key, host, sentinel);
817 return (retval == sentinel) ? 0 : retval.toInt();
823 QString retval = GetSettingOnHost(key, host, sentinel);
824 return (retval == sentinel) ? 0.0 : retval.toDouble();
828 int &width,
int &height,
829 double &forced_aspect,
830 double &refresh_rate,
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);
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);
850 QString res = GetSetting(sRes);
854 QStringList slist = res.split(QString(
"x"));
857 if (2 == slist.size())
859 w = slist[0].toInt(&ok0);
860 h = slist[1].toInt(&ok1);
867 refresh_rate = GetFloatSetting(sRR);
868 forced_aspect = GetFloatSetting(sAspect);
874 int tmpWidth = GetNumSetting(sWidth, width);
878 int tmpHeight = GetNumSetting(sHeight, height);
890 double forced_aspect = 0;
891 double refresh_rate = 0.0;
892 GetResolutionSetting(
t, w, h, forced_aspect, refresh_rate, i);
903 const QString &key,
const QString &value)
905 QString mk = key.toLower();
906 QString mk2 =
d->m_localhostname +
' ' + mk;
908 if (
"dbschemaver" == mk)
910 LOG(VB_GENERAL, LOG_ERR,
911 QString(
"ERROR: Refusing to allow override for '%1'.").arg(key));
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();
928 QString mk = key.toLower();
929 QString mk2 =
d->m_localhostname +
' ' + mk;
931 d->m_settingsCacheLock.lockForWrite();
933 SettingsMap::iterator oit =
d->m_overriddenSettings.find(mk);
934 if (oit !=
d->m_overriddenSettings.end())
935 d->m_overriddenSettings.erase(oit);
937 SettingsMap::iterator sit =
d->m_settingsCache.find(mk);
938 if (sit !=
d->m_settingsCache.end())
939 d->m_settingsCache.erase(sit);
941 sit =
d->m_settingsCache.find(mk2);
942 if (sit !=
d->m_settingsCache.end())
943 d->m_settingsCache.erase(sit);
945 d->m_settingsCacheLock.unlock();
952 SettingsMap::iterator it = cache.find(myKey);
953 if (it != cache.end())
955 SettingsMap::const_iterator oit = overrides.constFind(myKey);
956 if (oit == overrides.constEnd())
958 LOG(VB_DATABASE, LOG_INFO,
959 QString(
"Clearing Settings Cache for '%1'.").arg(myKey));
964 LOG(VB_DATABASE, LOG_INFO,
965 QString(
"Clearing Cache of overridden '%1' ignored.")
973 d->m_settingsCacheLock.lockForWrite();
977 LOG(VB_DATABASE, LOG_INFO,
"Clearing Settings Cache.");
978 d->m_settingsCache.clear();
981 SettingsMap::const_iterator it =
d->m_overriddenSettings.cbegin();
982 for (; it !=
d->m_overriddenSettings.cend(); ++it)
984 QString mk2 =
d->m_localhostname +
' ' + it.key();
987 d->m_settingsCache[it.key()] = *it;
988 d->m_settingsCache[mk2] = *it;
993 QString myKey = _key.toLower();
994 clear(
d->m_settingsCache,
d->m_overriddenSettings, myKey);
997 QString mkl = myKey.section(QChar(
' '), 1);
999 clear(
d->m_settingsCache,
d->m_overriddenSettings, mkl);
1002 d->m_settingsCacheLock.unlock();
1008 LOG(VB_DATABASE, LOG_INFO,
"Enabling Settings Cache.");
1010 LOG(VB_DATABASE, LOG_INFO,
"Disabling Settings Cache.");
1012 d->m_useSettingsCache = activate;
1018 if (!HaveValidDatabase())
1024 while (!
d->m_delayedSettings.isEmpty())
1036 d->m_haveDBConnection = connected;
1045 d->m_haveSchema = schema;
1057 return d->m_haveSchema;
1069 return (
d->m_haveDBConnection &&
d->m_haveSchema);
static int ClearSettingsCache(const MythUtilCommandLineParser &)
Structure containing the basic Database parameters.
QString m_dbName
database name
QString m_dbPassword
DB password.
std::chrono::seconds m_wolReconnect
seconds to wait for reconnect
QString m_localHostName
name used for loading/saving settings
bool m_dbHostPing
No longer used.
QString m_dbUserName
DB user name.
QString m_wolCommand
command to use for wake-on-lan
bool m_wolEnabled
true if wake-on-lan params are used
int m_dbPort
database port
int m_wolRetry
times to retry to reconnect
QString m_dbHostName
database server
DB connection pool, used by MSqlQuery. Do not use directly.
QSqlQuery wrapper that fetches a DB connection from the connection pool.
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
QString executedQuery(void) const
QSqlError lastError(void) const
QVariant value(int i) const
bool isConnected(void) const
Only updated once during object creation.
QVariantList boundValues(void) const
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
QReadWriteLock m_settingsCacheLock
bool m_suppressDBMessages
volatile bool m_useSettingsCache
SettingsMap m_settingsCache
Permanent settings in the DB and overridden settings.
SettingsMap m_overriddenSettings
Overridden this session only.
QList< SingleSetting > m_delayedSettings
Settings which should be written to the database as soon as it becomes available.
DatabaseParams m_dbParams
Current database host & WOL details.
void SetSuppressDBMessages(bool bUpgraded)
QString GetSetting(const QString &_key, const QString &defaultval)
void ActivateSettingsCache(bool activate=true)
QString GetSettingOnHost(const QString &_key, const QString &_host, const QString &defaultval)
bool GetBoolSetting(const QString &key, bool defaultval)
void SaveSetting(const QString &key, int newValue)
QString GetHostName(void) const
void OverrideSettingForSession(const QString &key, const QString &newValue)
Overrides the given setting for the execution time of the process.
static MythDB * getMythDB()
void IgnoreDatabase(bool bIgnore)
int GetNumSettingOnHost(const QString &key, const QString &host, int defaultval)
bool ClearSetting(const QString &key)
double GetFloatSettingOnHost(const QString &key, const QString &host, double defaultval)
static QString DBErrorMessage(const QSqlError &err)
static QString GetError(const QString &where, const MSqlQuery &query)
static void destroyMythDB()
bool GetSettings(QMap< QString, QString > &_key_value_pairs)
static QString toCommaList(const QMap< QString, QVariant > &bindings, uint indent=0, uint softMaxColumn=80)
void SetDatabaseParams(const DatabaseParams ¶ms)
bool IsDatabaseIgnored(void) const
void SetHaveDBConnection(bool connected)
Set a flag indicating we have successfully connected to the database.
bool HaveValidDatabase(void) const
Returns true if we have successfully connected to the database and that database has tables.
DatabaseParams GetDatabaseParams(void) const
void WriteDelayedSettings(void)
MDBManager * GetDBManager(void)
static void DBError(const QString &where, const MSqlQuery &query)
void SetLocalHostname(const QString &name)
void SetHaveSchema(bool schema)
Set a flag indicating that we have discovered tables and that this therefore not a new empty database...
void ClearSettingsCache(const QString &key=QString())
bool SuppressDBMessages(void) const
double GetFloatSetting(const QString &key, double defaultval)
bool ClearSettingOnHost(const QString &key, const QString &host)
bool SaveSettingOnHost(const QString &key, const QString &newValue, const QString &host)
void GetResolutionSetting(const QString &type, int &width, int &height, double &forced_aspect, double &refresh_rate, int index=-1)
QString GetDatabaseName() const
bool HaveSchema(void) const
Get a flag indicating that we have discovered tables and that this therefore not a new empty database...
void ClearOverrideSettingForSession(const QString &key)
Clears session Overrides for the given setting.
int GetNumSetting(const QString &key, int defaultval)
bool SaveDatabaseParams(const DatabaseParams ¶ms, bool force)
static const QString kDefaultWOL
static const QString kDefaultDB
static const iso6937table * d
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
MythDB * GetMythTestDB(const QString &testname)
static void clear(SettingsMap &cache, SettingsMap &overrides, const QString &myKey)
QHash< QString, QString > SettingsMap
static const int settings_reserve
const char *const kClearSettingValue
const char *const kSentinelValue
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Convenience inline random number generator functions.
static QString indent(uint level)
uint32_t MythRandom()
generate 32 random bits
static QString toCommaList(const QVector< uint > &list)