5#include <QReadWriteLock>
12#include <QRegularExpression>
13#include <QStandardPaths>
16#include "mythconfig.h"
67 params.m_dbHostPing =
false;
70 QStandardPaths::writableLocation(QStandardPaths::TempLocation) +
72 QString(
"mythtv_%1.%2.sqlite3")
73 .arg(testname).arg(
MythRandom(),8,16,QLatin1Char(
'0'));
75 params.m_dbName =
":memory:";
77 params.m_dbType =
"QSQLITE";
78 db->SetDatabaseParams(params);
129 LOG(VB_DATABASE, LOG_INFO,
"Destroying MythDBPrivate");
143 return &(
d->m_dbmanager);
149 QMap<QString, QVariant>::const_iterator it = bindings.begin();
150 if (it == bindings.end())
154 QString str = QString(
"%1").arg(
"",
indent);
155 for (; it != bindings.end(); ++it)
157 QString val = (*it).toString();
163#
if QT_VERSION < QT_VERSION_CHECK(6,0,0)
164 it->type() == QVariant::String
166 it->typeId() == QMetaType::QString
170 val = (it->toString().isNull()) ?
171 "NULL" : QString(
"\"%1\"").arg(val);
173 const QString curBinding = it.key() +
'=' + val +
',';
174 if ((curColumn >
indent) &&
175 ((curBinding.length() + curColumn) > maxColumn))
178 str += QString(
"%1").arg(
"",
indent);
187 curColumn += curBinding.length();
189 str = str.left(str.length() - 1);
197 QString str = QString(
"DB Error (%1):\n").arg(where);
199 str +=
"Query was:\n";
201#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
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())
210 auto match = iter.next();
211 namedBindings[match.captured()] = numberedBindings.isEmpty()
213 : numberedBindings.takeFirst();
219 str +=
"Bindings were:\n";
222 str += DBErrorMessage(query.
lastError());
228 LOG(VB_GENERAL, LOG_ERR, GetError(where, query));
234 return "No error type from QSqlError? Strange...";
236 return QString(
"Driver error was [%1/%2]:\n"
238 "Database error was:\n"
241 .arg(err.nativeErrorCode(),
248 return d->m_dbParams.m_dbName;
253 return d->m_dbParams;
258 d->m_dbParams = params;
266 if (
force || (params !=
d->m_dbParams))
283 if (addr.setAddress(dbHostName))
285 addr.setScopeId(QString());
286 dbHostName = addr.toString();
301 success = config.Save();
304 d->m_dbParams = params;
307 GetDBManager()->CloseDatabases();
315 if (
d->m_localhostname != name.toLower())
317 d->m_localhostname = name.toLower();
324 return d->m_localhostname;
329 d->m_ignoreDatabase = bIgnore;
334 return d->m_ignoreDatabase;
339 d->m_suppressDBMessages = bUpgraded;
344 return d->m_suppressDBMessages;
349 (void) SaveSettingOnHost(key,
350 QString::number(newValue),
d->m_localhostname);
355 (void) SaveSettingOnHost(key, newValue,
d->m_localhostname);
359 const QString &newValueRaw,
362 QString loc = QString(
"SaveSettingOnHost('%1') ").arg(key);
365 LOG(VB_GENERAL, LOG_ERR, loc +
"- Illegal null key");
369 QString newValue = (newValueRaw.isNull()) ?
"" : newValueRaw;
371 if (
d->m_ignoreDatabase)
373 if (host.toLower() ==
d->m_localhostname)
376 OverrideSettingForSession(key, newValue);
378 ClearOverrideSettingForSession(key);
383 if (!HaveValidDatabase())
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");
393 d->m_delayedSettings.append(setting);
397 bool success =
false;
405 query.
prepare(
"DELETE FROM settings WHERE value = :KEY "
406 "AND hostname = :HOSTNAME ;");
410 query.
prepare(
"DELETE FROM settings WHERE value = :KEY "
411 "AND hostname is NULL;");
433 query.
prepare(
"INSERT INTO settings (value,data,hostname) "
434 "VALUES ( :VALUE, :DATA, :HOSTNAME );");
438 query.
prepare(
"INSERT INTO settings (value,data ) "
439 "VALUES ( :VALUE, :DATA );");
450 if (!(
GetMythDB()->SuppressDBMessages()))
456 LOG(VB_GENERAL, LOG_ERR, loc +
"- database not open");
466 return ClearSettingOnHost(key,
d->m_localhostname);
476 QString key = _key.toLower();
477 QString value = defaultval;
479 d->m_settingsCacheLock.lockForRead();
480 if (
d->m_useSettingsCache)
482 SettingsMap::const_iterator it =
d->m_settingsCache.constFind(key);
483 if (it !=
d->m_settingsCache.constEnd())
486 d->m_settingsCacheLock.unlock();
490 SettingsMap::const_iterator it =
d->m_overriddenSettings.constFind(key);
491 if (it !=
d->m_overriddenSettings.constEnd())
494 d->m_settingsCacheLock.unlock();
497 d->m_settingsCacheLock.unlock();
499 if (
d->m_ignoreDatabase || !HaveValidDatabase())
509 "WHERE value = :KEY AND hostname = :HOSTNAME");
511 query.
bindValue(
":HOSTNAME",
d->m_localhostname);
515 value = query.
value(0).toString();
522 "WHERE value = :KEY AND hostname IS NULL");
527 value = query.
value(0).toString();
535 d->m_settingsCacheLock.lockForWrite();
538 if (!
d->m_settingsCache.contains(key))
539 d->m_settingsCache[key] = value;
540 d->m_settingsCacheLock.unlock();
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;
554 QMap<QString,bool>::iterator dit = done.begin();
555 kvit = _key_value_pairs.begin();
559 d->m_settingsCacheLock.lockForRead();
560 if (
d->m_useSettingsCache)
562 for (; kvit != _key_value_pairs.end(); ++dit, ++kvit)
564 SettingsMap::const_iterator it =
d->m_settingsCache.constFind(dit.key());
565 if (it !=
d->m_settingsCache.constEnd())
573 for (; kvit != _key_value_pairs.end(); ++dit, ++kvit)
575 SettingsMap::const_iterator it =
576 d->m_overriddenSettings.constFind(dit.key());
577 if (it !=
d->m_overriddenSettings.constEnd())
584 d->m_settingsCacheLock.unlock();
588 if (((
uint)done.size()) == done_cnt ||
d->m_ignoreDatabase)
593 kvit = _key_value_pairs.begin();
596 QMap<QString,KVIt> keymap;
597 for (; kvit != _key_value_pairs.end(); ++dit, ++kvit)
602 const QString& key = dit.key();
603 if (!key.contains(
"'"))
605 keylist += QString(
"'%1',").arg(key);
611 *kvit = GetSetting(key, *kvit);
615 if (keylist.isEmpty())
618 keylist = keylist.left(keylist.length() - 1);
623 "SELECT value, data, hostname "
625 "WHERE (hostname = '%1' OR hostname IS NULL) AND "
627 "ORDER BY hostname DESC")
628 .arg(
d->m_localhostname, keylist)))
630 if (!
d->m_suppressDBMessages)
631 DBError(
"GetSettings", query);
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();
643 if (
d->m_useSettingsCache)
645 d->m_settingsCacheLock.lockForWrite();
646 for (
auto it = keymap.cbegin(); it != keymap.cend(); ++it)
648 QString key = it.key();
649 QString value = **it;
653 if (!
d->m_settingsCache.contains(key))
657 d->m_settingsCache[key] = value;
660 d->m_settingsCacheLock.unlock();
669 QString val = QString::number(
static_cast<int>(defaultval));
670 QString retval = GetSetting(key, val);
672 return retval.toInt() > 0;
677 QString val = QString::number(defaultval);
678 QString retval = GetSetting(key, val);
680 return retval.toInt();
685 QString val = QString::number(defaultval);
686 QString retval = GetSetting(key, val);
688 return retval.toDouble();
694 QString retval = GetSetting(key, sentinel);
695 return (retval == sentinel) ?
"" : retval;
701 QString retval = GetSetting(key, sentinel);
702 if (retval == sentinel)
704 return retval.toInt() > 0;
710 QString retval = GetSetting(key, sentinel);
711 return (retval == sentinel) ? 0 : retval.toInt();
717 QString retval = GetSetting(key, sentinel);
718 return (retval == sentinel) ? 0.0 : retval.toDouble();
722 const QString &defaultval)
724 QString key = _key.toLower();
725 QString host = _host.toLower();
726 QString value = defaultval;
727 QString myKey = host +
' ' + key;
729 d->m_settingsCacheLock.lockForRead();
730 if (
d->m_useSettingsCache)
732 SettingsMap::const_iterator it =
d->m_settingsCache.constFind(myKey);
733 if (it !=
d->m_settingsCache.constEnd())
736 d->m_settingsCacheLock.unlock();
740 SettingsMap::const_iterator it =
d->m_overriddenSettings.constFind(myKey);
741 if (it !=
d->m_overriddenSettings.constEnd())
744 d->m_settingsCacheLock.unlock();
747 d->m_settingsCacheLock.unlock();
749 if (
d->m_ignoreDatabase)
755 if (!
d->m_suppressDBMessages)
757 LOG(VB_GENERAL, LOG_ERR,
758 QString(
"Database not open while trying to "
759 "load setting: %1").arg(key));
767 "WHERE value = :VALUE AND hostname = :HOSTNAME");
773 value = query.
value(0).toString();
780 d->m_settingsCacheLock.lockForWrite();
781 if (!
d->m_settingsCache.contains(myKey))
782 d->m_settingsCache[myKey] = value;
783 d->m_settingsCacheLock.unlock();
792 QString val = QString::number(defaultval);
793 QString retval = GetSettingOnHost(key, host, val);
795 return retval.toInt();
799 const QString &key,
const QString &host,
double defaultval)
801 QString val = QString::number(defaultval);
802 QString retval = GetSettingOnHost(key, host, val);
804 return retval.toDouble();
810 QString retval = GetSettingOnHost(key, host, sentinel);
811 return (retval == sentinel) ?
"" : retval;
817 QString retval = GetSettingOnHost(key, host, sentinel);
818 return (retval == sentinel) ? 0 : retval.toInt();
824 QString retval = GetSettingOnHost(key, host, sentinel);
825 return (retval == sentinel) ? 0.0 : retval.toDouble();
829 int &width,
int &height,
830 double &forced_aspect,
831 double &refresh_rate,
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);
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);
851 QString res = GetSetting(sRes);
855 QStringList slist = res.split(QString(
"x"));
858 if (2 == slist.size())
860 w = slist[0].toInt(&ok0);
861 h = slist[1].toInt(&ok1);
868 refresh_rate = GetFloatSetting(sRR);
869 forced_aspect = GetFloatSetting(sAspect);
875 int tmpWidth = GetNumSetting(sWidth, width);
879 int tmpHeight = GetNumSetting(sHeight, height);
891 double forced_aspect = 0;
892 double refresh_rate = 0.0;
893 GetResolutionSetting(
t, w, h, forced_aspect, refresh_rate, i);
904 const QString &key,
const QString &value)
906 QString mk = key.toLower();
907 QString mk2 =
d->m_localhostname +
' ' + mk;
909 if (
"dbschemaver" == mk)
911 LOG(VB_GENERAL, LOG_ERR,
912 QString(
"ERROR: Refusing to allow override for '%1'.").arg(key));
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();
929 QString mk = key.toLower();
930 QString mk2 =
d->m_localhostname +
' ' + mk;
932 d->m_settingsCacheLock.lockForWrite();
934 SettingsMap::iterator oit =
d->m_overriddenSettings.find(mk);
935 if (oit !=
d->m_overriddenSettings.end())
936 d->m_overriddenSettings.erase(oit);
938 SettingsMap::iterator sit =
d->m_settingsCache.find(mk);
939 if (sit !=
d->m_settingsCache.end())
940 d->m_settingsCache.erase(sit);
942 sit =
d->m_settingsCache.find(mk2);
943 if (sit !=
d->m_settingsCache.end())
944 d->m_settingsCache.erase(sit);
946 d->m_settingsCacheLock.unlock();
953 SettingsMap::iterator it = cache.find(myKey);
954 if (it != cache.end())
956 SettingsMap::const_iterator oit = overrides.constFind(myKey);
957 if (oit == overrides.constEnd())
959 LOG(VB_DATABASE, LOG_INFO,
960 QString(
"Clearing Settings Cache for '%1'.").arg(myKey));
965 LOG(VB_DATABASE, LOG_INFO,
966 QString(
"Clearing Cache of overridden '%1' ignored.")
974 d->m_settingsCacheLock.lockForWrite();
978 LOG(VB_DATABASE, LOG_INFO,
"Clearing Settings Cache.");
979 d->m_settingsCache.clear();
982 SettingsMap::const_iterator it =
d->m_overriddenSettings.cbegin();
983 for (; it !=
d->m_overriddenSettings.cend(); ++it)
985 QString mk2 =
d->m_localhostname +
' ' + it.key();
988 d->m_settingsCache[it.key()] = *it;
989 d->m_settingsCache[mk2] = *it;
994 QString myKey = _key.toLower();
995 clear(
d->m_settingsCache,
d->m_overriddenSettings, myKey);
998 QString mkl = myKey.section(QChar(
' '), 1);
1000 clear(
d->m_settingsCache,
d->m_overriddenSettings, mkl);
1003 d->m_settingsCacheLock.unlock();
1009 LOG(VB_DATABASE, LOG_INFO,
"Enabling Settings Cache.");
1011 LOG(VB_DATABASE, LOG_INFO,
"Disabling Settings Cache.");
1013 d->m_useSettingsCache = activate;
1019 if (!HaveValidDatabase())
1025 while (!
d->m_delayedSettings.isEmpty())
1037 d->m_haveDBConnection = connected;
1046 d->m_haveSchema = schema;
1058 return d->m_haveSchema;
1070 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)