You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
246 lines
9.3 KiB
246 lines
9.3 KiB
// databasesettingspage.cpp — Database maintenance page in settings |
|
#include "databasesettingspage.h" |
|
#include "../db/db_manager.h" |
|
#include <QVBoxLayout> |
|
#include <QHBoxLayout> |
|
#include <QFormLayout> |
|
#include <QLabel> |
|
#include <QFrame> |
|
#include <QFileInfo> |
|
#include <QTimer> |
|
|
|
DatabaseSettingsPage::DatabaseSettingsPage(DbManager* db, QWidget* parent) |
|
: QWidget(parent) |
|
, m_db(db) |
|
{ |
|
auto* layout = new QVBoxLayout(this); |
|
layout->setSpacing(10); |
|
layout->setContentsMargins(0, 0, 0, 0); |
|
|
|
auto* title = new QLabel("Database Maintenance", this); |
|
title->setStyleSheet("font-weight: bold; font-size: 13px;"); |
|
layout->addWidget(title); |
|
|
|
// --- DB info --- |
|
auto* infoLayout = new QFormLayout(); |
|
infoLayout->setSpacing(4); |
|
|
|
QString dbPath = "—"; |
|
QString dbSize = "—"; |
|
QString walSize = "—"; |
|
if (m_db && m_db->m_db) { |
|
const char* fname = sqlite3_db_filename(m_db->m_db, "main"); |
|
if (fname && fname[0]) { |
|
dbPath = QString::fromUtf8(fname); |
|
QFileInfo fi(dbPath); |
|
if (fi.exists()) { |
|
qint64 sz = fi.size(); |
|
if (sz < 1024) dbSize = QString("%1 B").arg(sz); |
|
else if (sz < 1024 * 1024) dbSize = QString("%1 KB").arg(sz / 1024); |
|
else dbSize = QString("%1 MB").arg(sz / (1024.0 * 1024.0), 0, 'f', 2); |
|
} |
|
QFileInfo walFi(dbPath + "-wal"); |
|
if (walFi.exists()) { |
|
qint64 wsz = walFi.size(); |
|
if (wsz < 1024) walSize = QString("%1 B").arg(wsz); |
|
else if (wsz < 1024 * 1024) walSize = QString("%1 KB").arg(wsz / 1024); |
|
else walSize = QString("%1 MB").arg(wsz / (1024.0 * 1024.0), 0, 'f', 2); |
|
} else { |
|
walSize = "n/a"; |
|
} |
|
} |
|
} |
|
m_filePathLabel = new QLabel(dbPath, this); |
|
m_filePathLabel->setStyleSheet("font-size: 11px; color: gray;"); |
|
m_filePathLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); |
|
infoLayout->addRow("File:", m_filePathLabel); |
|
|
|
m_fileSizeLabel = new QLabel(dbSize, this); |
|
m_fileSizeLabel->setStyleSheet("font-size: 11px;"); |
|
infoLayout->addRow("Size:", m_fileSizeLabel); |
|
|
|
m_walSizeLabel = new QLabel(walSize, this); |
|
m_walSizeLabel->setStyleSheet("font-size: 11px;"); |
|
infoLayout->addRow("WAL:", m_walSizeLabel); |
|
|
|
layout->addLayout(infoLayout); |
|
|
|
// --- Separator --- |
|
auto* sep = new QFrame(this); |
|
sep->setFrameShape(QFrame::HLine); |
|
sep->setFrameShadow(QFrame::Sunken); |
|
layout->addWidget(sep); |
|
|
|
// --- Buttons --- |
|
auto* btnLayout = new QVBoxLayout(); |
|
btnLayout->setSpacing(6); |
|
|
|
auto* quickRow = new QHBoxLayout(); |
|
m_quickBtn = new QPushButton("Quick Check", this); |
|
m_quickBtn->setStyleSheet( |
|
"QPushButton { border: 1px solid palette(mid); border-radius: 4px;" |
|
" padding: 5px 14px; font-size: 12px; background: palette(button); }" |
|
"QPushButton:hover { background: palette(light); }"); |
|
connect(m_quickBtn, &QPushButton::clicked, this, &DatabaseSettingsPage::onQuickCheck); |
|
quickRow->addWidget(m_quickBtn); |
|
auto* quickDesc = new QLabel("Scans structure without verifying indexes (fast)", this); |
|
quickDesc->setStyleSheet("color: gray; font-size: 11px;"); |
|
quickRow->addWidget(quickDesc, 1); |
|
btnLayout->addLayout(quickRow); |
|
|
|
auto* fullRow = new QHBoxLayout(); |
|
m_fullBtn = new QPushButton("Full Check", this); |
|
m_fullBtn->setStyleSheet(m_quickBtn->styleSheet()); |
|
connect(m_fullBtn, &QPushButton::clicked, this, &DatabaseSettingsPage::onFullCheck); |
|
fullRow->addWidget(m_fullBtn); |
|
auto* fullDesc = new QLabel("Verifies all data structures and indexes (thorough)", this); |
|
fullDesc->setStyleSheet("color: gray; font-size: 11px;"); |
|
fullRow->addWidget(fullDesc, 1); |
|
btnLayout->addLayout(fullRow); |
|
|
|
auto* optRow = new QHBoxLayout(); |
|
m_optimizeBtn = new QPushButton("Optimize", this); |
|
m_optimizeBtn->setStyleSheet(m_quickBtn->styleSheet()); |
|
connect(m_optimizeBtn, &QPushButton::clicked, this, &DatabaseSettingsPage::onOptimize); |
|
optRow->addWidget(m_optimizeBtn); |
|
auto* optDesc = new QLabel("Update query planner statistics for better performance", this); |
|
optDesc->setStyleSheet("color: gray; font-size: 11px;"); |
|
optRow->addWidget(optDesc, 1); |
|
btnLayout->addLayout(optRow); |
|
|
|
layout->addLayout(btnLayout); |
|
|
|
// --- Status --- |
|
m_statusLabel = new QLabel("", this); |
|
m_statusLabel->setStyleSheet("font-size: 12px; margin-top: 4px;"); |
|
layout->addWidget(m_statusLabel); |
|
|
|
// --- Results --- |
|
m_resultText = new QPlainTextEdit(this); |
|
m_resultText->setReadOnly(true); |
|
m_resultText->setMaximumHeight(120); |
|
m_resultText->setStyleSheet( |
|
"QPlainTextEdit { font-family: monospace; font-size: 11px;" |
|
" background: palette(base); border: 1px solid palette(mid); border-radius: 4px;" |
|
" padding: 4px; }"); |
|
m_resultText->setPlaceholderText("Run a check to see results..."); |
|
layout->addWidget(m_resultText); |
|
|
|
layout->addStretch(); |
|
|
|
if (!m_db || !m_db->m_db) { |
|
m_statusLabel->setText("Database not connected"); |
|
m_statusLabel->setStyleSheet("font-size: 12px; font-weight: bold; color: red;"); |
|
m_quickBtn->setEnabled(false); |
|
m_fullBtn->setEnabled(false); |
|
m_optimizeBtn->setEnabled(false); |
|
qWarning("DatabaseSettingsPage: DB not ready m_db=%p m_db->m_db=%p", (void*)m_db, m_db ? (void*)m_db->m_db : nullptr); |
|
} |
|
} |
|
|
|
void DatabaseSettingsPage::refreshStatus() { |
|
if (!m_db || !m_db->m_db) { |
|
m_statusLabel->setText("Database not connected"); |
|
m_statusLabel->setStyleSheet("font-size: 12px; font-weight: bold; color: red;"); |
|
m_quickBtn->setEnabled(false); |
|
m_fullBtn->setEnabled(false); |
|
m_optimizeBtn->setEnabled(false); |
|
return; |
|
} |
|
m_quickBtn->setEnabled(true); |
|
m_fullBtn->setEnabled(true); |
|
m_optimizeBtn->setEnabled(true); |
|
const char* fname = sqlite3_db_filename(m_db->m_db, "main"); |
|
if (fname && fname[0]) { |
|
QString dbPath = QString::fromUtf8(fname); |
|
m_filePathLabel->setText(dbPath); |
|
QFileInfo fi(dbPath); |
|
if (fi.exists()) { |
|
qint64 sz = fi.size(); |
|
if (sz < 1024) m_fileSizeLabel->setText(QString("%1 B").arg(sz)); |
|
else if (sz < 1024 * 1024) m_fileSizeLabel->setText(QString("%1 KB").arg(sz / 1024)); |
|
else m_fileSizeLabel->setText(QString("%1 MB").arg(sz / (1024.0 * 1024.0), 0, 'f', 2)); |
|
QFileInfo walFi(dbPath + "-wal"); |
|
if (walFi.exists()) { |
|
qint64 wsz = walFi.size(); |
|
if (wsz < 1024) m_walSizeLabel->setText(QString("%1 B").arg(wsz)); |
|
else if (wsz < 1024 * 1024) m_walSizeLabel->setText(QString("%1 KB").arg(wsz / 1024)); |
|
else m_walSizeLabel->setText(QString("%1 MB").arg(wsz / (1024.0 * 1024.0), 0, 'f', 2)); |
|
} else { m_walSizeLabel->setText("n/a"); } |
|
} |
|
} |
|
} |
|
|
|
void DatabaseSettingsPage::setBusy(bool busy) { |
|
m_quickBtn->setEnabled(!busy); |
|
m_fullBtn->setEnabled(!busy); |
|
m_optimizeBtn->setEnabled(!busy); |
|
if (busy) { |
|
m_statusLabel->setText("Checking..."); |
|
m_statusLabel->setStyleSheet("font-size: 12px; margin-top: 4px; color: gray;"); |
|
} |
|
} |
|
|
|
void DatabaseSettingsPage::onQuickCheck() { |
|
if (!m_db || !m_db->m_db) { |
|
m_statusLabel->setText("Database not open"); |
|
m_statusLabel->setStyleSheet("font-size: 12px; color: red;"); |
|
return; |
|
} |
|
setBusy(true); |
|
QTimer::singleShot(50, this, [this]() { |
|
IntegrityResult r = m_db->integrityCheckQuick(); |
|
showResult("Quick Check", r); |
|
}); |
|
} |
|
|
|
void DatabaseSettingsPage::onFullCheck() { |
|
if (!m_db || !m_db->m_db) { |
|
m_statusLabel->setText("Database not open"); |
|
m_statusLabel->setStyleSheet("font-size: 12px; color: red;"); |
|
return; |
|
} |
|
setBusy(true); |
|
QTimer::singleShot(50, this, [this]() { |
|
IntegrityResult r = m_db->integrityCheckFull(); |
|
showResult("Full Check", r); |
|
}); |
|
} |
|
|
|
void DatabaseSettingsPage::onOptimize() { |
|
if (!m_db || !m_db->m_db) { |
|
m_statusLabel->setText("Database not open"); |
|
m_statusLabel->setStyleSheet("font-size: 12px; color: red;"); |
|
return; |
|
} |
|
setBusy(true); |
|
QTimer::singleShot(50, this, [this]() { |
|
IntegrityResult r = m_db->integrityOptimize(); |
|
showResult("Optimize", r); |
|
}); |
|
} |
|
|
|
void DatabaseSettingsPage::showResult(const QString& checkName, const IntegrityResult& result) { |
|
m_quickBtn->setEnabled(true); |
|
m_fullBtn->setEnabled(true); |
|
m_optimizeBtn->setEnabled(true); |
|
|
|
if (result.ok) { |
|
m_statusLabel->setText(QString("%1: OK (%2 ms)") |
|
.arg(checkName).arg(result.elapsedMs)); |
|
m_statusLabel->setStyleSheet("font-size: 12px; font-weight: bold; color: green;"); |
|
} else { |
|
m_statusLabel->setText(QString("%1: FAILED (%2 ms)") |
|
.arg(checkName).arg(result.elapsedMs)); |
|
m_statusLabel->setStyleSheet("font-size: 12px; font-weight: bold; color: red;"); |
|
} |
|
|
|
if (result.errors.isEmpty()) { |
|
m_resultText->setPlainText(result.message.isEmpty() ? "ok" : result.message); |
|
} else { |
|
QStringList lines; |
|
lines.append(result.message); |
|
lines.append(result.errors); |
|
m_resultText->setPlainText(lines.join('\n')); |
|
} |
|
}
|
|
|