11#include <QCoreApplication>
14#include <QSqlDatabase>
15#include <QtConcurrentRun>
36 qDebug () <<
"checking" << dbPath;
39 auto db = QSqlDatabase::addDatabase (
"QSQLITE"_qs, connName);
40 const auto remGuard =
MakeScopeGuard ([connName] { QSqlDatabase::removeDatabase (connName); });
42 db.setDatabaseName (dbPath);
45 qWarning () <<
"cannot open the DB, but that's not the kind of errors we're solving.";
49 QSqlQuery pragma { db };
50 static const auto checkQuery = qgetenv (
"LC_THOROUGH_SQLITE_CHECK") ==
"1" ?
51 "PRAGMA integrity_check;"_qs :
52 "PRAGMA quick_check;"_qs;
53 const auto isGood = pragma.exec (checkQuery) &&
55 pragma.value (0) ==
"ok";
56 qDebug () <<
"done checking" << dbPath <<
"; db is good?" << isGood;
65 const QFileInfo fi { dbPath };
66 const auto filesize = fi.size ();
70 qDebug () <<
"db size:" << filesize
71 <<
"free space:" << available;
72 if (available >= filesize)
81 co_return co_await QtConcurrent::run (CheckSync, dbPath);
86 [[maybe_unused]]
const auto hasEnoughSpace =
co_await CheckRecoverSpace (dbPath);
87 const auto& newPath = dbPath +
".new";
88 if (QFile::exists (newPath))
91 const auto dumpProcResult =
co_await DumpSqlite (dbPath, newPath);
92 [[maybe_unused]]
const auto dumpProcSuccess =
co_await WithHandler (dumpProcResult,
95 const auto oldSize = QFileInfo { dbPath }.size ();
96 const auto newSize = QFileInfo { newPath }.size ();
98 const auto& backupPath = dbPath +
".bak";
99 if (!QFile::rename (dbPath, backupPath))
103 while (!QFile::rename (newPath, dbPath))
105 qCritical () <<
"unable to rename" << newPath <<
"→" << dbPath;
106 const auto& msg = Tr::tr (
"Unable to rename %1 to %2. Please check %2 does not exist, and hit OK.")
107 .arg (newPath, dbPath);
108 QMessageBox::critical (
nullptr,
"LeechCraft"_qs, msg);
111 co_return RecoverFinished { .OldFileSize_ = oldSize, .NewFileSize_ = newSize };
116 QString GetRecoverFailureMessage (
const RecoverFailed& failure)
118 return Visit (failure,
119 [&] (RecoverNoSpace space)
121 return Tr::tr (
"Not enough space available: "
122 "%1 free while the restored file is expected to be around %2. "
123 "Please either free some disk space on this partition "
124 "and retry or cancel the restore process.")
127 [&] (
const RecoverTargetExists& exists)
129 return Tr::tr (
"Target file %1 already exists, please remove it manually and retry.")
132 [&] (
const RecoverOtherFailure& other)
134 return other.Message_;
143 const auto result =
co_await Recover (dbPath);
144 if (result.IsRight ())
147 const auto& question = Tr::tr (
"Unable to dump corrupted SQLite database %1.").arg (
FormatName (dbPath)) +
149 GetRecoverFailureMessage (result.GetLeft ());
150 if (QMessageBox::question (
nullptr, diaTitle, question, QMessageBox::Retry | QMessageBox::Cancel) == QMessageBox::Cancel)
QString GenConnectionName(const QString &base)
Generates an unique thread-safe connection name.
Task< RecoverResult_t > Recover(QString dbPath)
Task< RecoverResult_t > RecoverWithUserInteraction(QString dbPath, QString diaTitle)
Either< Failed, Succeeded > CheckResult_t
std::variant< RecoverNoSpace, RecoverTargetExists, RecoverOtherFailure > RecoverFailed
Task< CheckResult_t > Check(QString dbPath)
detail::ScopeGuard< F > MakeScopeGuard(const F &f)
Returns an object performing passed function on scope exit.
Task< Either< QString, Void > > DumpSqlite(QString from, QString to)
detail::EitherAwaiter< L, R, F > WithHandler(const Either< L, R > &either, F &&errorHandler)
auto Visit(const Either< Left, Right > &either, Args &&... args)
SpaceInfo GetSpaceInfo(const QString &path)
Returns the disk space info of the partition containing path.
QString FormatName(const QString &name)
HTML-formats the name to let the user know it is not a part of the fixed dialog text.
QString MakePrettySize(qint64 sourcesize)
Makes a formatted size from number.
quint64 Available_
How much space is available to the current user.
A proper void type, akin to unit (or ()) type in functional languages.