File indexing completed on 2025-01-05 04:47:01

0001 /*
0002  * SPDX-FileCopyrightText: 2013 Daniel Vrátil <dvratil@redhat.com>
0003  *
0004  * SPDX-License-Identifier: LGPL-2.1-or-later
0005  *
0006  */
0007 
0008 #include "storagedebugger.h"
0009 #include "storagedebuggeradaptor.h"
0010 
0011 #include <QSqlError>
0012 #include <QSqlQuery>
0013 #include <QSqlRecord>
0014 
0015 #include <QDBusConnection>
0016 
0017 Akonadi::Server::StorageDebugger *Akonadi::Server::StorageDebugger::mSelf = nullptr;
0018 QMutex Akonadi::Server::StorageDebugger::mMutex;
0019 
0020 using namespace Akonadi::Server;
0021 
0022 Q_DECLARE_METATYPE(QList<QList<QVariant>>)
0023 Q_DECLARE_METATYPE(DbConnection)
0024 Q_DECLARE_METATYPE(QList<DbConnection>)
0025 
0026 QDBusArgument &operator<<(QDBusArgument &arg, const DbConnection &con)
0027 {
0028     arg.beginStructure();
0029     arg << con.id << con.name << con.start << con.trxName << con.transactionStart;
0030     arg.endStructure();
0031     return arg;
0032 }
0033 
0034 const QDBusArgument &operator>>(const QDBusArgument &arg, DbConnection &con)
0035 {
0036     arg.beginStructure();
0037     arg >> con.id >> con.name >> con.start >> con.trxName >> con.transactionStart;
0038     arg.endStructure();
0039     return arg;
0040 }
0041 
0042 namespace
0043 {
0044 QList<DbConnection>::Iterator findConnection(QList<DbConnection> &vec, qint64 id)
0045 {
0046     return std::find_if(vec.begin(), vec.end(), [id](const DbConnection &con) {
0047         return con.id == id;
0048     });
0049 }
0050 
0051 } // namespace
0052 
0053 StorageDebugger *StorageDebugger::instance()
0054 {
0055     mMutex.lock();
0056     if (mSelf == nullptr) {
0057         mSelf = new StorageDebugger();
0058     }
0059     mMutex.unlock();
0060 
0061     return mSelf;
0062 }
0063 
0064 StorageDebugger::StorageDebugger()
0065 {
0066     qDBusRegisterMetaType<QList<QList<QVariant>>>();
0067     qDBusRegisterMetaType<DbConnection>();
0068     qDBusRegisterMetaType<QList<DbConnection>>();
0069     new StorageDebuggerAdaptor(this);
0070     QDBusConnection::sessionBus().registerObject(QStringLiteral("/storageDebug"), this, QDBusConnection::ExportAdaptors);
0071 }
0072 
0073 StorageDebugger::~StorageDebugger() = default;
0074 
0075 void StorageDebugger::enableSQLDebugging(bool enable)
0076 {
0077     mEnabled = enable;
0078 }
0079 
0080 void StorageDebugger::writeToFile(const QString &file)
0081 {
0082     mFile = std::make_unique<QFile>(file);
0083     mFile->open(QIODevice::WriteOnly);
0084 }
0085 
0086 void StorageDebugger::addConnection(qint64 id, const QString &name)
0087 {
0088     QMutexLocker locker(&mMutex);
0089     const qint64 timestamp = QDateTime::currentMSecsSinceEpoch();
0090     mConnections.push_back({id, name, timestamp, QString(), 0LL});
0091     if (mEnabled) {
0092         Q_EMIT connectionOpened(id, timestamp, name);
0093     }
0094 }
0095 
0096 void StorageDebugger::removeConnection(qint64 id)
0097 {
0098     QMutexLocker locker(&mMutex);
0099     auto con = findConnection(mConnections, id);
0100     if (con == mConnections.end()) {
0101         return;
0102     }
0103     mConnections.erase(con);
0104 
0105     if (mEnabled) {
0106         Q_EMIT connectionClosed(id, QDateTime::currentMSecsSinceEpoch());
0107     }
0108 }
0109 
0110 void StorageDebugger::changeConnection(qint64 id, const QString &name)
0111 {
0112     QMutexLocker locker(&mMutex);
0113     auto con = findConnection(mConnections, id);
0114     if (con == mConnections.end()) {
0115         return;
0116     }
0117     con->name = name;
0118 
0119     if (mEnabled) {
0120         Q_EMIT connectionChanged(id, name);
0121     }
0122 }
0123 
0124 void StorageDebugger::addTransaction(qint64 connectionId, const QString &name, uint duration, const QString &error)
0125 {
0126     QMutexLocker locker(&mMutex);
0127     auto con = findConnection(mConnections, connectionId);
0128     if (con == mConnections.end()) {
0129         return;
0130     }
0131     con->trxName = name;
0132     con->transactionStart = QDateTime::currentMSecsSinceEpoch();
0133 
0134     if (mEnabled) {
0135         Q_EMIT transactionStarted(connectionId, name, con->transactionStart, duration, error);
0136     }
0137 }
0138 
0139 void StorageDebugger::removeTransaction(qint64 connectionId, bool commit, uint duration, const QString &error)
0140 {
0141     QMutexLocker locker(&mMutex);
0142     auto con = findConnection(mConnections, connectionId);
0143     if (con == mConnections.end()) {
0144         return;
0145     }
0146     con->trxName.clear();
0147     con->transactionStart = 0;
0148 
0149     if (mEnabled) {
0150         Q_EMIT transactionFinished(connectionId, commit, QDateTime::currentMSecsSinceEpoch(), duration, error);
0151     }
0152 }
0153 
0154 QList<DbConnection> StorageDebugger::connections() const
0155 {
0156     return mConnections;
0157 }
0158 
0159 void StorageDebugger::queryExecuted(qint64 connectionId, const QSqlQuery &query, int duration)
0160 {
0161     if (!mEnabled) {
0162         return;
0163     }
0164 
0165     const qint64 seq = ++mSequence;
0166     if (query.lastError().isValid()) {
0167         Q_EMIT queryExecuted(seq,
0168                              connectionId,
0169                              QDateTime::currentMSecsSinceEpoch(),
0170                              duration,
0171                              query.executedQuery(),
0172                              query.boundValues(),
0173                              0,
0174                              QList<QList<QVariant>>(),
0175                              query.lastError().text());
0176         return;
0177     }
0178 
0179     QSqlQuery q(query);
0180     QList<QVariantList> result;
0181 
0182     if (q.first()) {
0183         const QSqlRecord record = q.record();
0184         QVariantList row;
0185         const int numRecords = record.count();
0186         row.reserve(numRecords);
0187         for (int i = 0; i < numRecords; ++i) {
0188             row << record.fieldName(i);
0189         }
0190         result << row;
0191 
0192         int cnt = 0;
0193         do {
0194             const QSqlRecord record = q.record();
0195             QVariantList row;
0196             const int numRecords = record.count();
0197             row.reserve(numRecords);
0198             for (int i = 0; i < numRecords; ++i) {
0199                 row << record.value(i);
0200             }
0201             result << row;
0202         } while (q.next() && ++cnt < 1000);
0203     }
0204 
0205     const int querySize = query.isSelect() ? query.size() : query.numRowsAffected();
0206     Q_EMIT queryExecuted(seq,
0207                          connectionId,
0208                          QDateTime::currentMSecsSinceEpoch(),
0209                          duration,
0210                          query.executedQuery(),
0211                          query.boundValues(),
0212                          querySize,
0213                          result,
0214                          QString());
0215 
0216     if (mFile && mFile->isOpen()) {
0217         QTextStream out(mFile.get());
0218         out << query.executedQuery() << " " << duration << "ms\n";
0219     }
0220 
0221     // Reset the query
0222     q.seek(-1, false);
0223 }
0224 
0225 #include "moc_storagedebugger.cpp"