7#include "dbconfigmysql.h" 
    8#include "akonadiserver_debug.h" 
   11#include "private/standarddirs_p.h" 
   13#include <QCoreApplication> 
   15#include <QRegularExpression> 
   19#include <QStandardPaths> 
   23using namespace Akonadi::Server;
 
   25#define MYSQL_MIN_MAJOR 5 
   26#define MYSQL_MIN_MINOR 1 
   28#define MYSQL_VERSION_CHECK(major, minor, patch) (((major) << 16) | ((minor) << 8) | (patch)) 
   30static const QString s_mysqlSocketFileName = QStringLiteral(
"mysql.socket");
 
   31static const QString s_initConnection = QStringLiteral(
"initConnectionMysql");
 
   33DbConfigMysql::DbConfigMysql(
const QString &configFile)
 
   38DbConfigMysql::~DbConfigMysql() = 
default;
 
   40QString DbConfigMysql::driverName()
 const 
   42    return QStringLiteral(
"QMYSQL");
 
   45QString DbConfigMysql::databaseName()
 const 
   50QString DbConfigMysql::databasePath()
 const 
   55void DbConfigMysql::setDatabasePath(
const QString &path, QSettings &settings)
 
   59    settings.
setValue(QStringLiteral(
"DataDir"), mDataDir);
 
   64static QString findExecutable(
const QString &bin)
 
   66    static const QStringList mysqldSearchPath = {
 
   67#ifdef MYSQLD_SCRIPTS_PATH 
   68        QStringLiteral(MYSQLD_SCRIPTS_PATH),
 
   70        QStringLiteral(
"/usr/bin"),
 
   71        QStringLiteral(
"/usr/sbin"),
 
   72        QStringLiteral(
"/usr/local/sbin"),
 
   73        QStringLiteral(
"/usr/local/libexec"),
 
   74        QStringLiteral(
"/usr/libexec"),
 
   75        QStringLiteral(
"/opt/mysql/libexec"),
 
   76        QStringLiteral(
"/opt/local/lib/mysql5/bin"),
 
   77        QStringLiteral(
"/opt/mysql/sbin"),
 
   86bool DbConfigMysql::init(QSettings &settings, 
bool storeSettings, 
const QString &dbPathOverride)
 
   89    QString defaultHostName;
 
   90    QString defaultOptions;
 
   91    QString defaultServerPath;
 
   92    QString defaultCleanShutdownCommand;
 
   95    const QString socketDirectory = Utils::preferredSocketDirectory(StandardDirs::saveDir(
"data", QStringLiteral(
"db_misc")), s_mysqlSocketFileName.
length());
 
   98    const bool defaultInternalServer = 
true;
 
   99#ifdef MYSQLD_EXECUTABLE 
  101        defaultServerPath = QStringLiteral(MYSQLD_EXECUTABLE);
 
  104    if (defaultServerPath.
isEmpty()) {
 
  105        defaultServerPath = findExecutable(QStringLiteral(
"mysqld"));
 
  108    const QString mysqladminPath = findExecutable(QStringLiteral(
"mysqladmin"));
 
  109    if (!mysqladminPath.
isEmpty()) {
 
  111        defaultCleanShutdownCommand = QStringLiteral(
"%1 --defaults-file=%2/mysql.conf --socket=%3/%4 shutdown")
 
  112                                          .
arg(mysqladminPath, StandardDirs::saveDir(
"data"), socketDirectory, s_mysqlSocketFileName);
 
  118    const QString defaultDataDir = dbPathOverride.
isEmpty() ? StandardDirs::saveDir(
"data", QStringLiteral(
"db_data")) : dbPathOverride;
 
  120    mMysqlInstallDbPath = findExecutable(QStringLiteral(
"mysql_install_db"));
 
  121    qCDebug(AKONADISERVER_LOG) << 
"Found mysql_install_db: " << mMysqlInstallDbPath;
 
  123    mMysqlCheckPath = findExecutable(QStringLiteral(
"mysqlcheck"));
 
  124    qCDebug(AKONADISERVER_LOG) << 
"Found mysqlcheck: " << mMysqlCheckPath;
 
  126    mMysqlUpgradePath = findExecutable(QStringLiteral(
"mysql_upgrade"));
 
  127    qCDebug(AKONADISERVER_LOG) << 
"Found mysql_upgrade: " << mMysqlUpgradePath;
 
  129    mInternalServer = settings.
value(QStringLiteral(
"QMYSQL/StartServer"), defaultInternalServer).
toBool();
 
  131    if (mInternalServer) {
 
  132        defaultOptions = QStringLiteral(
"UNIX_SOCKET=%1/%2").
arg(socketDirectory, s_mysqlSocketFileName);
 
  139    mHostName = settings.
value(QStringLiteral(
"Host"), defaultHostName).
toString();
 
  140    mUserName = settings.
value(QStringLiteral(
"User")).
toString();
 
  141    mPassword = settings.
value(QStringLiteral(
"Password")).
toString();
 
  142    mConnectionOptions = settings.
value(QStringLiteral(
"Options"), defaultOptions).
toString();
 
  143    mDataDir = settings.
value(QStringLiteral(
"DataDir"), defaultDataDir).
toString();
 
  144    mMysqldPath = settings.
value(QStringLiteral(
"ServerPath"), defaultServerPath).
toString();
 
  145    mCleanServerShutdownCommand = settings.
value(QStringLiteral(
"CleanServerShutdownCommand"), defaultCleanShutdownCommand).
toString();
 
  149    if (mInternalServer) {
 
  150        mConnectionOptions = defaultOptions;
 
  152        mDatabaseName = QStringLiteral(
"akonadi");
 
  154    if (mInternalServer && (mMysqldPath.isEmpty() || !
QFile::exists(mMysqldPath))) {
 
  155        mMysqldPath = defaultServerPath;
 
  158    qCDebug(AKONADISERVER_LOG) << 
"Using mysqld:" << mMysqldPath;
 
  163        settings.
setValue(QStringLiteral(
"Name"), mDatabaseName);
 
  164        settings.
setValue(QStringLiteral(
"Host"), mHostName);
 
  165        settings.
setValue(QStringLiteral(
"Options"), mConnectionOptions);
 
  166        if (!mMysqldPath.isEmpty()) {
 
  167            settings.
setValue(QStringLiteral(
"ServerPath"), mMysqldPath);
 
  169        settings.
setValue(QStringLiteral(
"StartServer"), mInternalServer);
 
  170        settings.
setValue(QStringLiteral(
"DataDir"), mDataDir);
 
  176    if (mInternalServer) {
 
  185bool DbConfigMysql::isAvailable(QSettings &settings)
 
  191    if (!init(settings, 
false)) {
 
  195    if (mInternalServer && (mMysqldPath.isEmpty() || !
QFile::exists(mMysqldPath))) {
 
  202void DbConfigMysql::apply(QSqlDatabase &database)
 
  204    if (!mDatabaseName.isEmpty()) {
 
  207    if (!mHostName.isEmpty()) {
 
  210    if (!mUserName.isEmpty()) {
 
  213    if (!mPassword.isEmpty()) {
 
  223bool DbConfigMysql::useInternalServer()
 const 
  225    return mInternalServer;
 
  228bool DbConfigMysql::startInternalServer()
 
  232    const QString akDir = StandardDirs::saveDir(
"data");
 
  234    const QString socketDirectory = Utils::preferredSocketDirectory(StandardDirs::saveDir(
"data", QStringLiteral(
"db_misc")), s_mysqlSocketFileName.
length());
 
  235    const QString socketFile = QStringLiteral(
"%1/%2").arg(socketDirectory, s_mysqlSocketFileName);
 
  236    const QString pidFileName = QStringLiteral(
"%1/mysql.pid").arg(socketDirectory);
 
  240    const QString globalConfig = StandardDirs::locateResourceFile(
"config", QStringLiteral(
"mysql-global.conf"));
 
  241    const QString localConfig = StandardDirs::locateResourceFile(
"config", QStringLiteral(
"mysql-local.conf"));
 
  242    const QString actualConfig = StandardDirs::saveDir(
"data") + QLatin1StringView(
"/mysql.conf");
 
  243    qCDebug(AKONADISERVER_LOG) << 
" globalConfig : " << globalConfig << 
" localConfig : " << localConfig << 
" actualConfig : " << actualConfig;
 
  245        qCCritical(AKONADISERVER_LOG) << 
"Did not find MySQL server default configuration (mysql-global.conf)";
 
  255        if (Utils::getDirectoryFileSystem(mDataDir) == QLatin1StringView(
"btrfs")) {
 
  256            Utils::disableCoW(mDataDir);
 
  261    if (mMysqldPath.isEmpty()) {
 
  262        qCCritical(AKONADISERVER_LOG) << 
"mysqld not found. Please verify your installation";
 
  270    const unsigned int localVersion = parseCommandLineToolsVersion();
 
  271    if (localVersion == 0x000000) {
 
  272        qCCritical(AKONADISERVER_LOG) << 
"Failed to detect mysqld version!";
 
  276    const bool isMariaDB = localVersion >= MYSQL_VERSION_CHECK(10, 0, 0);
 
  277    qCDebug(AKONADISERVER_LOG).nospace() << 
"mysqld reports version " << (localVersion >> 16) << 
"." << ((localVersion >> 8) & 0x0000FF) << 
"." 
  278                                         << (localVersion & 0x0000FF) << 
" (" << (isMariaDB ? 
"MariaDB" : 
"Oracle MySQL") << 
")";
 
  280    QFile actualFile(actualConfig);
 
  282    if ((QFileInfo(globalConfig).lastModified() > QFileInfo(actualFile).lastModified())
 
  283        || (QFileInfo(localConfig).lastModified() > QFileInfo(actualFile).lastModified())) {
 
  284        QFile globalFile(globalConfig);
 
  285        QFile localFile(localConfig);
 
  287            actualFile.write(globalFile.readAll());
 
  290                    actualFile.write(localFile.readAll());
 
  297            qCCritical(AKONADISERVER_LOG) << 
"Unable to create MySQL server configuration file.";
 
  298            qCCritical(AKONADISERVER_LOG) << 
"This means that either the default configuration file (mysql-global.conf) was not readable";
 
  299            qCCritical(AKONADISERVER_LOG) << 
"or the target file (mysql.conf) could not be written.";
 
  309    if (allowedPerms != actualFile.permissions()) {
 
  310        actualFile.setPermissions(allowedPerms);
 
  313    if (mDataDir.isEmpty()) {
 
  314        qCCritical(AKONADISERVER_LOG) << 
"Akonadi server was not able to create database data directory";
 
  319        qCCritical(AKONADISERVER_LOG) << 
"Akonadi server was not able to create database log directory";
 
  324    if (socketDirectory.
isEmpty()) {
 
  325        qCCritical(AKONADISERVER_LOG) << 
"Akonadi server was not able to create database misc directory";
 
  330    if (socketDirectory.
length() >= 90) {
 
  331        qCCritical(AKONADISERVER_LOG) << 
"MySQL cannot deal with a socket path this long. Path was: " << socketDirectory;
 
  337    QFile pidFile(pidFileName);
 
  339        qCDebug(AKONADISERVER_LOG) << 
"Found a mysqld pid file, checking whether the server is still running...";
 
  340        QByteArray pid = pidFile.readLine().trimmed();
 
  344        bool serverIsRunning = 
false;
 
  346            const QByteArray 
stat = proc.readAll();
 
  347            const QList<QByteArray> stats = 
stat.split(
' ');
 
  348            if (stats.
count() > 1) {
 
  352                const QString expectedProcName = QFileInfo(mMysqldPath).fileName().left(15);
 
  356                    qCWarning(AKONADISERVER_LOG) << 
"mysqld for Akonadi is already running, trying to connect to it.";
 
  357                    serverIsRunning = 
true;
 
  363        if (!serverIsRunning) {
 
  364            qCDebug(AKONADISERVER_LOG) << 
"No mysqld process with specified PID is running. Removing the pidfile and starting a new instance...";
 
  373    QStringList arguments;
 
  374    arguments << QStringLiteral(
"--defaults-file=%1/mysql.conf").arg(akDir);
 
  375    arguments << QStringLiteral(
"--datadir=%1/").arg(mDataDir);
 
  377    arguments << QStringLiteral(
"--socket=%1").arg(socketFile);
 
  378    arguments << QStringLiteral(
"--pid-file=%1").arg(pidFileName);
 
  383    const QString errorLogFile = mDataDir + 
QDir::separator() + QLatin1StringView(
"mysql.err");
 
  389        const QFileInfo errorLog(errorLogFile);
 
  390        if (errorLog.exists()) {
 
  391            QFile logFile(errorLog.absoluteFilePath());
 
  392            QFile oldLogFile(mDataDir + 
QDir::separator() + QLatin1StringView(
"mysql.err.old"));
 
  394                oldLogFile.write(logFile.readAll());
 
  399                qCCritical(AKONADISERVER_LOG) << 
"Failed to open MySQL error log.";
 
  404        const QString confFile = StandardDirs::locateResourceFile(
"config", QStringLiteral(
"mysql-global.conf"));
 
  407                initializeMariaDBDatabase(confFile, mDataDir);
 
  408            } 
else if (localVersion >= MYSQL_VERSION_CHECK(5, 7, 6)) {
 
  409                initializeMySQL5_7_6Database(confFile, mDataDir);
 
  411                initializeMySQLDatabase(confFile, mDataDir);
 
  415        qCDebug(AKONADISERVER_LOG) << 
"Executing:" << mMysqldPath << arguments.
join(QLatin1Char(
' '));
 
  416        mDatabaseProcess = std::make_unique<QProcess>();
 
  417        mDatabaseProcess->start(mMysqldPath, arguments);
 
  418        if (!mDatabaseProcess->waitForStarted()) {
 
  419            qCCritical(AKONADISERVER_LOG) << 
"Could not start database server!";
 
  420            qCCritical(AKONADISERVER_LOG) << 
"executable:" << mMysqldPath;
 
  421            qCCritical(AKONADISERVER_LOG) << 
"arguments:" << arguments;
 
  422            qCCritical(AKONADISERVER_LOG) << 
"process error:" << mDatabaseProcess->errorString();
 
  434        qCDebug(AKONADISERVER_LOG) << 
"Found " << qPrintable(s_mysqlSocketFileName) << 
" file, reconnecting to the database";
 
  444            qCCritical(AKONADISERVER_LOG) << 
"Invalid database object during database server startup";
 
  449        for (
int i = 0; i < 120; ++i) {
 
  454            if (mDatabaseProcess && mDatabaseProcess->waitForFinished(500)) {
 
  455                qCCritical(AKONADISERVER_LOG) << 
"Database process exited unexpectedly during initial connection!";
 
  456                qCCritical(AKONADISERVER_LOG) << 
"executable:" << mMysqldPath;
 
  457                qCCritical(AKONADISERVER_LOG) << 
"arguments:" << arguments;
 
  458                qCCritical(AKONADISERVER_LOG) << 
"stdout:" << mDatabaseProcess->readAllStandardOutput();
 
  459                qCCritical(AKONADISERVER_LOG) << 
"stderr:" << mDatabaseProcess->readAllStandardError();
 
  460                qCCritical(AKONADISERVER_LOG) << 
"exit code:" << mDatabaseProcess->exitCode();
 
  461                qCCritical(AKONADISERVER_LOG) << 
"process error:" << mDatabaseProcess->errorString();
 
  462                qCCritical(AKONADISERVER_LOG) << 
"See" << errorLogFile << 
"for more details";
 
  468            if (!mMysqlCheckPath.isEmpty()) {
 
  470                        {QStringLiteral(
"--defaults-file=%1/mysql.conf").arg(akDir),
 
  471                         QStringLiteral(
"--check-upgrade"),
 
  472                         QStringLiteral(
"--auto-repair"),
 
  474                         QStringLiteral(
"--socket=%1/%2").arg(socketDirectory, s_mysqlSocketFileName),
 
  479            if (!mMysqlUpgradePath.isEmpty()) {
 
  481                        {QStringLiteral(
"--defaults-file=%1/mysql.conf").arg(akDir)
 
  484                         QStringLiteral(
"--socket=%1/%2").arg(socketDirectory, s_mysqlSocketFileName)
 
  492                if (!
query.exec(QStringLiteral(
"SELECT VERSION()")) || !
query.
first()) {
 
  493                    qCCritical(AKONADISERVER_LOG) << 
"Failed to verify database server version";
 
  494                    qCCritical(AKONADISERVER_LOG) << 
"Query error:" << 
query.lastError().text();
 
  495                    qCCritical(AKONADISERVER_LOG) << 
"Database error:" << db.
lastError().
text();
 
  501                if (versions.
count() < 3) {
 
  502                    qCCritical(AKONADISERVER_LOG) << 
"Invalid database server version: " << 
version;
 
  506                if (versions[0].toInt() < MYSQL_MIN_MAJOR || (versions[0].toInt() == MYSQL_MIN_MAJOR && versions[1].toInt() < MYSQL_MIN_MINOR)) {
 
  507                    qCCritical(AKONADISERVER_LOG) << 
"Unsupported MySQL version:";
 
  508                    qCCritical(AKONADISERVER_LOG) << 
"Current version:" << QStringLiteral(
"%1.%2").arg(versions[0], versions[1]);
 
  509                    qCCritical(AKONADISERVER_LOG) << 
"Minimum required version:" << QStringLiteral(
"%1.%2").arg(MYSQL_MIN_MAJOR).arg(MYSQL_MIN_MINOR);
 
  510                    qCCritical(AKONADISERVER_LOG) << 
"Please update your MySQL database server";
 
  513                    qCDebug(AKONADISERVER_LOG) << 
"MySQL version OK" 
  514                                               << 
"(required" << QStringLiteral(
"%1.%2").arg(MYSQL_MIN_MAJOR).arg(MYSQL_MIN_MINOR) << 
", available" 
  515                                               << QStringLiteral(
"%1.%2").arg(versions[0], versions[1]) << 
")";
 
  521                if (!
query.exec(QStringLiteral(
"USE %1").arg(mDatabaseName))) {
 
  522                    qCDebug(AKONADISERVER_LOG) << 
"Failed to use database" << mDatabaseName;
 
  523                    qCDebug(AKONADISERVER_LOG) << 
"Query error:" << 
query.lastError().text();
 
  524                    qCDebug(AKONADISERVER_LOG) << 
"Database error:" << db.
lastError().
text();
 
  525                    qCDebug(AKONADISERVER_LOG) << 
"Trying to create database now...";
 
  526                    if (!
query.exec(QStringLiteral(
"CREATE DATABASE akonadi"))) {
 
  527                        qCCritical(AKONADISERVER_LOG) << 
"Failed to create database";
 
  528                        qCCritical(AKONADISERVER_LOG) << 
"Query error:" << 
query.lastError().text();
 
  529                        qCCritical(AKONADISERVER_LOG) << 
"Database error:" << db.
lastError().
text();
 
  536            qCCritical(AKONADISERVER_LOG) << 
"Failed to connect to database!";
 
  537            qCCritical(AKONADISERVER_LOG) << 
"Database error:" << db.
lastError().
text();
 
  550    qCCritical(AKONADISERVER_LOG) << 
"database server stopped unexpectedly";
 
  555    const QString socketDirectory = Utils::preferredSocketDirectory(StandardDirs::saveDir(
"data", QStringLiteral(
"db_misc")), s_mysqlSocketFileName.
length());
 
  556    const QString socketFile = QStringLiteral(
"%1/%2").
arg(socketDirectory, s_mysqlSocketFileName);
 
  563void DbConfigMysql::stopInternalServer()
 
  565    if (!mDatabaseProcess) {
 
  575    if (!mCleanServerShutdownCommand.isEmpty()) {
 
  577        if (mDatabaseProcess->waitForFinished(3000)) {
 
  582    mDatabaseProcess->terminate();
 
  583    const bool result = mDatabaseProcess->waitForFinished(3000);
 
  586        mDatabaseProcess->kill();
 
  590void DbConfigMysql::initSession(
const QSqlDatabase &database)
 
  592    QSqlQuery 
query(database);
 
  593    query.exec(QStringLiteral(
"SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED"));
 
  596int DbConfigMysql::parseCommandLineToolsVersion()
 const 
  598    QProcess mysqldProcess;
 
  599    mysqldProcess.
start(mMysqldPath, {QStringLiteral(
"--version")});
 
  603    QRegularExpression regexp(QStringLiteral(
"Ver ([0-9]+)\\.([0-9]+)\\.([0-9]+)"));
 
  604    auto match = regexp.match(out);
 
  605    if (!
match.hasMatch()) {
 
  609    return (
match.capturedView(1).toInt() << 16) | (
match.capturedView(2).toInt() << 8) | 
match.capturedView(3).toInt();
 
  612bool DbConfigMysql::initializeMariaDBDatabase(
const QString &confFile, 
const QString &dataDir)
 const 
  617    if (mMysqlInstallDbPath.isEmpty()) {
 
  618        return QDir().mkpath(dataDir);
 
  621    QFileInfo fi(mMysqlInstallDbPath);
 
  624    const QString baseDir = 
dir.absolutePath();
 
  626        == 
execute(mMysqlInstallDbPath,
 
  627                   {QStringLiteral(
"--defaults-file=%1").arg(confFile),
 
  628                    QStringLiteral(
"--force"),
 
  629                    QStringLiteral(
"--basedir=%1").arg(baseDir),
 
  630                    QStringLiteral(
"--datadir=%1/").arg(dataDir)});
 
  637bool DbConfigMysql::initializeMySQL5_7_6Database(
const QString &confFile, 
const QString &dataDir)
 const 
  641                   {QStringLiteral(
"--defaults-file=%1").arg(confFile), QStringLiteral(
"--initialize"), QStringLiteral(
"--datadir=%1/").arg(dataDir)});
 
  644bool DbConfigMysql::initializeMySQLDatabase(
const QString &confFile, 
const QString &dataDir)
 const 
  648    if (mMysqlInstallDbPath.isEmpty()) {
 
  649        return QDir().mkpath(dataDir);
 
  652    QFileInfo fi(mMysqlInstallDbPath);
 
  655    const QString baseDir = 
dir.absolutePath();
 
  661               {QStringLiteral(
"--defaults-file=%1").arg(confFile), QStringLiteral(
"--basedir=%1").arg(baseDir), QStringLiteral(
"--datadir=%1/").arg(dataDir)});
 
  664bool DbConfigMysql::disableConstraintChecks(
const QSqlDatabase &db)
 
  667    return query.exec(QStringLiteral(
"SET FOREIGN_KEY_CHECKS=0"));
 
  670bool DbConfigMysql::enableConstraintChecks(
const QSqlDatabase &db)
 
  673    return query.exec(QStringLiteral(
"SET FOREIGN_KEY_CHECKS=1"));
 
  676#include "moc_dbconfigmysql.cpp" 
A base class that provides an unique access layer to configuration and initialization of different da...
 
static QString defaultDatabaseName()
Returns the suggested default database name, if none is specified in the configuration already.
 
int execute(const QString &cmd, const QStringList &args) const
Calls QProcess::execute() and also prints the command and arguments via qCDebug()
 
Helper integration between Akonadi and Qt.
 
KSERVICE_EXPORT KService::List query(FilterFunc filterFunc)
 
KDB_EXPORT KDbVersionInfo version()
 
KCOREADDONS_EXPORT Result match(QStringView pattern, QStringView str)
 
KIOCORE_EXPORT StatJob * stat(const QUrl &url, JobFlags flags=DefaultFlags)
 
QString path(const QString &relativePath)
 
KIOCORE_EXPORT QString dir(const QString &fileClass)
 
bool exists() const const
 
bool exists() const const
 
qsizetype count() const const
 
T value(qsizetype i) const const
 
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
 
bool disconnect(const QMetaObject::Connection &connection)
 
int execute(const QString &program, const QStringList &arguments)
 
void finished(int exitCode, QProcess::ExitStatus exitStatus)
 
QByteArray readAllStandardOutput()
 
void start(OpenMode mode)
 
bool waitForFinished(int msecs)
 
void beginGroup(QAnyStringView prefix)
 
void setValue(QAnyStringView key, const QVariant &value)
 
QVariant value(QAnyStringView key) const const
 
QSqlDatabase addDatabase(QSqlDriver *driver, const QString &connectionName)
 
QSqlDriver * driver() const const
 
bool isValid() const const
 
QSqlError lastError() const const
 
void removeDatabase(const QString &connectionName)
 
void setConnectOptions(const QString &options)
 
void setDatabaseName(const QString &name)
 
void setHostName(const QString &host)
 
void setPassword(const QString &password)
 
void setUserName(const QString &name)
 
virtual bool hasFeature(DriverFeature feature) const const=0
 
QString text() const const
 
QString findExecutable(const QString &executableName, const QStringList &paths)
 
QString arg(Args &&... args) const const
 
QString fromLatin1(QByteArrayView str)
 
QString fromLocal8Bit(QByteArrayView str)
 
bool isEmpty() const const
 
qsizetype length() const const
 
QString join(QChar separator) const const
 
void msleep(unsigned long msecs)
 
bool toBool() const const
 
QString toString() const const