File indexing completed on 2025-02-02 04:56:53

0001 /***************************************************************************
0002  * SPDX-FileCopyrightText: 2022 S. MANKOWSKI stephane@mankowski.fr
0003  * SPDX-FileCopyrightText: 2022 G. DE BURE support@mankowski.fr
0004  * SPDX-License-Identifier: GPL-3.0-or-later
0005  ***************************************************************************/
0006 /** @file
0007  * This file is a plugin for debug.
0008  *
0009  * @author Stephane MANKOWSKI / Guillaume DE BURE
0010  */
0011 #include "skgdebugpluginwidget.h"
0012 
0013 #include <qdom.h>
0014 #include <qjsengine.h>
0015 #include <qscriptengine.h>
0016 
0017 #include "skgdocument.h"
0018 #include "skgmainpanel.h"
0019 #include "skgservices.h"
0020 #include "skgtraces.h"
0021 #include "skgtransactionmng.h"
0022 
0023 SKGDebugPluginWidget::SKGDebugPluginWidget(QWidget* iParent, SKGDocument* iDocument)
0024     : SKGTabPage(iParent, iDocument)
0025 {
0026     SKGTRACEINFUNC(10)
0027     if (iDocument == nullptr) {
0028         return;
0029     }
0030 
0031     ui.setupUi(this);
0032 
0033     // Set icons
0034     ui.kSQLPushButton->setIcon(SKGServices::fromTheme(QStringLiteral("system-run")));
0035     ui.kSQLTransactionPushButton->setIcon(SKGServices::fromTheme(QStringLiteral("system-run")));
0036     ui.kRefreshViewsAndIndexes->setIcon(SKGServices::fromTheme(QStringLiteral("view-refresh")));
0037 
0038     // Fill combo box
0039     ui.kExplainCmb->addItem(SKGServices::fromTheme(QStringLiteral("system-run")), i18nc("Execute an SQL query", "Execute"));
0040     ui.kExplainCmb->addItem(SKGServices::fromTheme(QStringLiteral("system-run")), i18nc("Execute an SQL queries (one per line)", "Execute multi queries"));
0041     ui.kExplainCmb->addItem(SKGServices::fromTheme(QStringLiteral("help-hint")), i18nc("Explain an SQL query", "Explain"));
0042     ui.kExplainCmb->addItem(SKGServices::fromTheme(QStringLiteral("games-hint")), i18nc("Explain the SQL query plan", "Explain query plan"));
0043     ui.kExplainCmb->addItem(SKGServices::fromTheme(QStringLiteral("media-playback-start")), i18nc("Execute script", "Execute script [%1]", "javascript"));
0044     ui.kInput->setVisible(false);
0045 
0046     // Set level trace
0047     ui.kTraceLevel->setValue(SKGTraces::SKGLevelTrace);
0048 
0049     // Set profiling mode
0050     ui.kEnableProfilingChk->setCheckState(SKGTraces::SKGPerfo ? Qt::Checked : Qt::Unchecked);
0051 
0052     // Init debug page
0053     QStringList tables;
0054     ui.kSQLInput->addItem(QStringLiteral("SELECT * FROM sqlite_master;"));
0055     iDocument->getDistinctValues(QStringLiteral("sqlite_master"), QStringLiteral("name"), QStringLiteral("type in ('table', 'view')"), tables);
0056     int nb = tables.count();
0057     for (int i = 0; i < nb; ++i) {
0058         ui.kSQLInput->addItem("SELECT * FROM " % tables.at(i) % ';');
0059     }
0060     ui.kSQLInput->addItem(QStringLiteral("ANALYZE;"));
0061     ui.kSQLInput->addItem(QStringLiteral("PRAGMA integrity_check;"));
0062     for (int i = 0; i < nb; ++i) {
0063         ui.kSQLInput->addItem("PRAGMA table_info(" % tables.at(i) % ");");
0064         ui.kSQLInput->addItem("PRAGMA index_list(" % tables.at(i) % ");");
0065     }
0066 
0067     iDocument->getDistinctValues(QStringLiteral("sqlite_master"), QStringLiteral("name"), QStringLiteral("type='index'"), tables);
0068     nb = tables.count();
0069     for (int i = 0; i < nb; ++i) {
0070         ui.kSQLInput->addItem("PRAGMA index_info(" % tables.at(i) % ");");
0071     }
0072     connect(ui.kTraceLevel, &QSlider::valueChanged, this, &SKGDebugPluginWidget::onTraceLevelModified);
0073     connect(ui.kEnableProfilingChk, &QCheckBox::stateChanged, this, &SKGDebugPluginWidget::onProfilingModeChanged);
0074     connect(ui.kExplainCmb, static_cast<void (SKGComboBox::*)(int)>(&SKGComboBox::currentIndexChanged), this, &SKGDebugPluginWidget::onModeChanged);
0075     connect(ui.kSQLPushButton, &QPushButton::clicked, this, &SKGDebugPluginWidget::onExecuteSqlOrder);
0076     connect(ui.kSQLTransactionPushButton, &QPushButton::clicked, this, &SKGDebugPluginWidget::onExecuteSqlOrderInTransaction);
0077     connect(ui.kRefreshViewsAndIndexes, &QPushButton::clicked, this, &SKGDebugPluginWidget::onRefreshViewsAndIndexes);
0078 }
0079 
0080 SKGDebugPluginWidget::~SKGDebugPluginWidget()
0081 {
0082     SKGTRACEINFUNC(10)
0083 }
0084 
0085 QString SKGDebugPluginWidget::getState()
0086 {
0087     SKGTRACEINFUNC(10)
0088     QDomDocument doc(QStringLiteral("SKGML"));
0089     QDomElement root = doc.createElement(QStringLiteral("parameters"));
0090     doc.appendChild(root);
0091 
0092     root.setAttribute(QStringLiteral("explain"), ui.kExplainCmb->currentIndex());
0093     root.setAttribute(QStringLiteral("enableProfiling"), ui.kEnableProfilingChk->checkState() == Qt::Checked ? QStringLiteral("Y") : QStringLiteral("N"));
0094     root.setAttribute(QStringLiteral("levelTraces"), ui.kTraceLevel->value());
0095     root.setAttribute(QStringLiteral("sqlOrder"), ui.kSQLInput->currentText());
0096 
0097     return doc.toString();
0098 }
0099 
0100 void SKGDebugPluginWidget::setState(const QString& iState)
0101 {
0102     SKGTRACEINFUNC(10)
0103     QDomDocument doc(QStringLiteral("SKGML"));
0104     doc.setContent(iState);
0105     QDomElement root = doc.documentElement();
0106 
0107     QString explain = root.attribute(QStringLiteral("explain"));
0108     QString enableProfiling = root.attribute(QStringLiteral("enableProfiling"));
0109     QString levelTraces = root.attribute(QStringLiteral("levelTraces"));
0110     QString sqlOrder = root.attribute(QStringLiteral("sqlOrder"));
0111     QString sqlResult = root.attribute(QStringLiteral("sqlResult"));
0112 
0113     if (!explain.isEmpty()) {
0114         ui.kExplainCmb->setCurrentIndex(SKGServices::stringToInt(explain == QStringLiteral("Y") ? QStringLiteral("1") : explain));
0115     }
0116     if (!enableProfiling.isEmpty()) {
0117         ui.kEnableProfilingChk->setCheckState(enableProfiling == QStringLiteral("Y") ? Qt::Checked : Qt::Unchecked);
0118     }
0119     if (!levelTraces.isEmpty()) {
0120         ui.kTraceLevel->setValue(SKGServices::stringToInt(levelTraces));
0121     }
0122     ui.kSQLInput->setText(sqlOrder);
0123     ui.kSQLResult->setPlainText(sqlResult);
0124 }
0125 
0126 void SKGDebugPluginWidget::onExecuteSqlOrderInTransaction()
0127 {
0128     onExecuteSqlOrder(true);
0129 }
0130 
0131 SKGError SKGDebugPluginWidget::executeSqlOrders(const QStringList& iSQL, QString& oOutput)
0132 {
0133     SKGError err;
0134     int nb = iSQL.count();
0135     for (int i = 0; i < nb; ++i) {
0136         auto sql = iSQL[i].trimmed();
0137         if (!sql.isEmpty()) {
0138             oOutput += sql + '\n';
0139 
0140             QString oResult;
0141             double time = SKGServices::getMicroTime();
0142             err = getDocument()->dumpSelectSqliteOrder(sql, oResult);
0143             time = SKGServices::getMicroTime() - time;
0144 
0145             oOutput += oResult;
0146             oOutput += i18nc("Display the execution time needed by an SQL query", "\nExecution time: %1 ms", SKGServices::doubleToString(time));
0147             oOutput += "\n\n";
0148         }
0149     }
0150 
0151     IFKO(err) {
0152         oOutput += err.getFullMessageWithHistorical();
0153     }
0154     return err;
0155 }
0156 
0157 void SKGDebugPluginWidget::onExecuteSqlOrder(bool iInTransaction)
0158 {
0159     SKGTRACEINFUNC(10)
0160     SKGError err;
0161     int exp = ui.kExplainCmb->currentIndex();
0162     if (exp == 4) {
0163         // Script execution
0164         ui.kSQLResult->clear();
0165         QJSEngine myEngine;
0166         // skgresult.setText(skgdocument.getUniqueIdentifier())
0167         // skgerror=skgdocument.sendMessage(QStringLiteral("Hello"))
0168         // skgerror=skgdocument.sendMessage(QStringLiteral("Hello"))
0169         // skgmainpanel.closeAllOtherPages(skgmainpanel.currentPage())
0170         auto t = myEngine.globalObject();
0171         t.setProperty(QStringLiteral("skgresult"), myEngine.newQObject(ui.kSQLResult));
0172         t.setProperty(QStringLiteral("skgdocument"), myEngine.newQObject(getDocument()));
0173         // t.setProperty(QStringLiteral("skgerror"), myEngine.newQObject(&err));
0174         t.setProperty(QStringLiteral("skgmainpanel"), myEngine.newQObject(SKGMainPanel::getMainPanel()));
0175 
0176         // Finally execute the scripting code.
0177         myEngine.evaluate(ui.kInput->toPlainText());
0178     } else if (exp == 1) {
0179         // SQL multi lines
0180         auto sqls = ui.kInput->toPlainText().split('\n');
0181         QString oResultGlobal;
0182         if (iInTransaction) {
0183             SKGBEGINTRANSACTION(*getDocument(), i18nc("Display an SQL command from the debug plugin", "SQL command from debug plugin"), err)
0184             IFOKDO(err, err = SKGDebugPluginWidget::executeSqlOrders(sqls, oResultGlobal))
0185         } else {
0186             QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
0187             err = SKGDebugPluginWidget::executeSqlOrders(sqls, oResultGlobal);
0188             QApplication::restoreOverrideCursor();
0189         }
0190 
0191         IFKO(err) {
0192             oResultGlobal += err.getFullMessageWithHistorical();
0193         }
0194         ui.kSQLResult->setPlainText(oResultGlobal);
0195     } else {
0196         // SQL execution
0197         QString text = ui.kSQLInput->currentText();
0198         if (exp == 2) {
0199             text = "EXPLAIN " % text;
0200         } else if (exp == 3) {
0201             text = "EXPLAIN QUERY PLAN " % text;
0202         }
0203         QString oResult;
0204         double time = SKGServices::getMicroTime();
0205         if (iInTransaction) {
0206             SKGBEGINTRANSACTION(*getDocument(), i18nc("Display an SQL command from the debug plugin", "SQL command from debug plugin"), err)
0207             IFOKDO(err, getDocument()->dumpSelectSqliteOrder(text, oResult))
0208         } else {
0209             QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
0210             err = getDocument()->dumpSelectSqliteOrder(text, oResult);
0211             QApplication::restoreOverrideCursor();
0212         }
0213         time = SKGServices::getMicroTime() - time;
0214 
0215         oResult += i18nc("Display the execution time needed by an SQL query", "\nExecution time: %1 ms", SKGServices::doubleToString(time));
0216 
0217         IFOK(err) {
0218             ui.kSQLResult->setPlainText(oResult);
0219         } else {
0220             ui.kSQLResult->setPlainText(err.getFullMessageWithHistorical());
0221         }
0222     }
0223 }
0224 
0225 void SKGDebugPluginWidget::onTraceLevelModified()
0226 {
0227     SKGTRACEINFUNC(10)
0228     SKGTraces::SKGLevelTrace = ui.kTraceLevel->value();
0229 }
0230 
0231 void SKGDebugPluginWidget::onModeChanged()
0232 {
0233     SKGTRACEINFUNC(10)
0234     int exp = ui.kExplainCmb->currentIndex();
0235     ui.kInput->setVisible(exp == 4 || exp == 1);
0236     ui.kSQLInput->setVisible(!ui.kInput->isVisible());
0237 }
0238 
0239 void SKGDebugPluginWidget::onProfilingModeChanged()
0240 {
0241     SKGTRACEINFUNC(10)
0242     SKGTraces::SKGPerfo = (ui.kEnableProfilingChk->checkState() == Qt::Checked);
0243 }
0244 
0245 void SKGDebugPluginWidget::onRefreshViewsAndIndexes()
0246 {
0247     SKGTRACEINFUNC(10)
0248     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
0249     SKGError err;
0250     err = getDocument()->refreshViewsIndexesAndTriggers();
0251     IFKO(err) {
0252         ui.kSQLResult->setPlainText(err.getFullMessageWithHistorical());
0253     }
0254     QApplication::restoreOverrideCursor();
0255 }
0256 
0257