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"