File indexing completed on 2025-01-19 03:53:33

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2009-09-05
0007  * Description : Database engine gui error handler
0008  *
0009  * SPDX-FileCopyrightText: 2009-2010 by Holger Foerster <Hamsi2k at freenet dot de>
0010  * SPDX-FileCopyrightText: 2010-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0011  *
0012  * SPDX-License-Identifier: GPL-2.0-or-later
0013  *
0014  * ============================================================ */
0015 
0016 #include "dbengineguierrorhandler.h"
0017 
0018 // Qt includes
0019 
0020 #include <QEventLoop>
0021 #include <QMutexLocker>
0022 #include <QSqlDatabase>
0023 #include <QSqlError>
0024 #include <QWaitCondition>
0025 #include <QProgressDialog>
0026 #include <QMessageBox>
0027 #include <QApplication>
0028 #include <QPointer>
0029 #include <QTimer>
0030 
0031 // KDE includes
0032 
0033 #include <klocalizedstring.h>
0034 
0035 // Local includes
0036 
0037 #include "digikam_debug.h"
0038 
0039 namespace Digikam
0040 {
0041 
0042 class Q_DECL_HIDDEN DbEngineConnectionChecker::Private
0043 {
0044 
0045 public:
0046 
0047     explicit Private()
0048         : stop(false),
0049           success(false)
0050     {
0051     }
0052 
0053     bool               stop;
0054     bool               success;
0055 
0056     QMutex             mutex;
0057     QWaitCondition     condVar;
0058 
0059     DbEngineParameters parameters;
0060 };
0061 
0062 DbEngineConnectionChecker::DbEngineConnectionChecker(const DbEngineParameters& parameters)
0063     : d(new Private)
0064 {
0065     d->parameters = parameters;
0066 }
0067 
0068 DbEngineConnectionChecker::~DbEngineConnectionChecker()
0069 {
0070     delete d;
0071 }
0072 
0073 void DbEngineConnectionChecker::run()
0074 {
0075     QString databaseID(QLatin1String("ConnectionTest"));
0076 
0077     // NOTE: wrap this code into bracket to prevent QtSQL plugin warning. See bug #339074 for details.
0078     {
0079         QSqlDatabase databaseHandler = QSqlDatabase::addDatabase(d->parameters.databaseType, databaseID);
0080 
0081         databaseHandler.setHostName(d->parameters.hostName);
0082         databaseHandler.setPort(d->parameters.port);
0083         databaseHandler.setDatabaseName(d->parameters.databaseNameCore);
0084         databaseHandler.setUserName(d->parameters.userName);
0085         databaseHandler.setPassword(d->parameters.password);
0086         databaseHandler.setConnectOptions(d->parameters.connectOptions);
0087 
0088         int iteration = 1;
0089 
0090         while (!d->stop)
0091         {
0092             if (databaseHandler.open())
0093             {
0094                 d->stop    = true;
0095                 d->success = true;
0096                 databaseHandler.close();
0097                 break;
0098             }
0099             else
0100             {
0101                 Q_EMIT failedAttempt();
0102                 d->success = false;
0103                 qCDebug(DIGIKAM_DBENGINE_LOG) << "Error while opening the database. Error details ["
0104                                               << databaseHandler.lastError() << "]";
0105                 QMutexLocker lock(&d->mutex);
0106 
0107                 if (!d->stop)
0108                 {
0109                     int waitingTime = qMin(2000, iteration++ * 200);
0110                     d->condVar.wait(&d->mutex, waitingTime);
0111                 }
0112             }
0113         }
0114     }
0115 
0116     QSqlDatabase::removeDatabase(databaseID);
0117 
0118     Q_EMIT done();
0119 }
0120 
0121 void DbEngineConnectionChecker::stopChecking()
0122 {
0123     QMutexLocker lock(&d->mutex);
0124     d->stop = true;
0125     d->condVar.wakeAll();
0126 }
0127 
0128 bool DbEngineConnectionChecker::checkSuccessful() const
0129 {
0130     return d->success;
0131 }
0132 
0133 // ---------------------------------------------------------------------------------------
0134 
0135 class Q_DECL_HIDDEN DbEngineGuiErrorHandler::Private
0136 {
0137 
0138 public:
0139 
0140     explicit Private()
0141         : checker(nullptr)
0142     {
0143     }
0144 
0145     QPointer<QProgressDialog>  dialog;
0146 
0147     DbEngineParameters         parameters;
0148     DbEngineConnectionChecker* checker;
0149 };
0150 
0151 DbEngineGuiErrorHandler::DbEngineGuiErrorHandler(const DbEngineParameters& parameters)
0152     : d(new Private)
0153 {
0154     d->parameters = parameters;
0155 }
0156 
0157 DbEngineGuiErrorHandler::~DbEngineGuiErrorHandler()
0158 {
0159     delete d;
0160 }
0161 
0162 bool DbEngineGuiErrorHandler::checkDatabaseConnection()
0163 {
0164     // now we try to connect periodically to the database
0165 
0166     d->checker = new DbEngineConnectionChecker(d->parameters);
0167     QEventLoop loop;
0168 
0169     QTimer timer;
0170     timer.setInterval(10000);
0171     timer.setSingleShot(true);
0172 
0173     connect(d->checker, &DbEngineConnectionChecker::failedAttempt,
0174             this, &DbEngineGuiErrorHandler::showProgressDialog);
0175 
0176     connect(&timer, &QTimer::timeout,
0177             this, &DbEngineGuiErrorHandler::showProgressDialog);
0178 
0179     connect(d->checker, &DbEngineConnectionChecker::done,
0180             &loop, &QEventLoop::quit);
0181 
0182     d->checker->start();
0183     timer.start();
0184     loop.exec();
0185 
0186     timer.stop();
0187 
0188     delete d->dialog;
0189 
0190     // ensure that the connection thread is closed
0191 
0192     d->checker->wait();
0193 
0194     bool result = d->checker->checkSuccessful();
0195     delete d->checker;
0196     return result;
0197 }
0198 
0199 void DbEngineGuiErrorHandler::showProgressDialog()
0200 {
0201     if (d->dialog || !d->checker)
0202     {
0203         return;
0204     }
0205 
0206     d->dialog = new QProgressDialog;
0207     d->dialog->setModal(true);
0208     d->dialog->setAttribute(Qt::WA_DeleteOnClose);
0209     d->dialog->setMinimum(0);
0210     d->dialog->setMaximum(0);
0211     d->dialog->setLabelText(i18n("Error while opening the database.\n"
0212                                  "digiKam will try to automatically reconnect to the database."));
0213 
0214     connect(d->dialog, SIGNAL(rejected()),
0215             d->checker, SLOT(stopChecking()));
0216 
0217     connect(d->dialog, SIGNAL(canceled()),
0218             d->checker, SLOT(stopChecking()));
0219 
0220     d->dialog->show();
0221 }
0222 
0223 void DbEngineGuiErrorHandler::connectionError(DbEngineErrorAnswer* answer, const QSqlError&, const QString&)
0224 {
0225     if (checkDatabaseConnection())
0226     {
0227         answer->connectionErrorContinueQueries();
0228     }
0229     else
0230     {
0231         answer->connectionErrorAbortQueries();
0232     }
0233 }
0234 
0235 void DbEngineGuiErrorHandler::consultUserForError(DbEngineErrorAnswer* answer, const QSqlError& error, const QString&)
0236 {
0237     // NOTE: not used at all currently.
0238 
0239     QWidget* const parent = QWidget::find(0);
0240 
0241     // Handle all other database errors
0242 
0243     QString message = i18n("<p><b>A database error occurred.</b></p>"
0244                            "Details:\n %1", error.text());
0245     QMessageBox::critical(parent, qApp->applicationName(), message);
0246     answer->connectionErrorAbortQueries();
0247 }
0248 
0249 } // namespace Digikam
0250 
0251 #include "moc_dbengineguierrorhandler.cpp"