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

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2007-03-18
0007  * Description : Database access wrapper.
0008  *
0009  * SPDX-FileCopyrightText: 2007-2008 by Marcel Wiesweg <marcel dot wiesweg at gmx 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 "thumbsdbaccess.h"
0017 
0018 // Qt includes
0019 
0020 #include <QMutex>
0021 #include <QMutexLocker>
0022 #include <QFileInfo>
0023 #include <QSqlDatabase>
0024 
0025 // KDE includes
0026 
0027 #include <klocalizedstring.h>
0028 
0029 // Local includes
0030 
0031 #include "digikam_debug.h"
0032 #include "thumbsdbbackend.h"
0033 #include "thumbsdb.h"
0034 #include "thumbsdbschemaupdater.h"
0035 #include "dbengineaccess.h"
0036 #include "dbengineerrorhandler.h"
0037 
0038 namespace Digikam
0039 {
0040 
0041 class Q_DECL_HIDDEN ThumbsDbAccessStaticPriv
0042 {
0043 public:
0044 
0045     explicit ThumbsDbAccessStaticPriv()
0046         : backend     (nullptr),
0047           db          (nullptr),
0048           initializing(false)
0049     {
0050     }
0051 
0052     ~ThumbsDbAccessStaticPriv()
0053     {
0054     };
0055 
0056     ThumbsDbBackend*   backend;
0057     ThumbsDb*          db;
0058     DbEngineParameters parameters;
0059     DbEngineLocking    lock;
0060     QString            lastError;
0061 
0062     bool               initializing;
0063 };
0064 
0065 ThumbsDbAccessStaticPriv* ThumbsDbAccess::d = nullptr;
0066 
0067 // -----------------------------------------------------------------------------
0068 
0069 class Q_DECL_HIDDEN ThumbsDbAccessMutexLocker
0070 
0071 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0072 
0073     : public QMutexLocker<QRecursiveMutex>
0074 
0075 #else
0076 
0077     : public QMutexLocker
0078 
0079 #endif
0080 
0081 {
0082 public:
0083 
0084     explicit ThumbsDbAccessMutexLocker(ThumbsDbAccessStaticPriv* const dd)
0085         : QMutexLocker(&dd->lock.mutex),
0086           d           (dd)
0087     {
0088         d->lock.lockCount++;
0089     }
0090 
0091     ~ThumbsDbAccessMutexLocker()
0092     {
0093         d->lock.lockCount--;
0094     }
0095 
0096 public:
0097 
0098     ThumbsDbAccessStaticPriv* const d;
0099 };
0100 
0101 // -----------------------------------------------------------------------------
0102 
0103 ThumbsDbAccess::ThumbsDbAccess()
0104 {
0105     // You will want to call setParameters before constructing ThumbsDbAccess.
0106 
0107     Q_ASSERT(d);
0108 
0109     d->lock.mutex.lock();
0110     d->lock.lockCount++;
0111 
0112     if (!d->backend->isOpen() && !d->initializing)
0113     {
0114         // avoid endless loops
0115 
0116         d->initializing = true;
0117 
0118         d->backend->open(d->parameters);
0119 
0120         d->initializing = false;
0121     }
0122 }
0123 
0124 ThumbsDbAccess::~ThumbsDbAccess()
0125 {
0126     d->lock.lockCount--;
0127     d->lock.mutex.unlock();
0128 }
0129 
0130 ThumbsDbAccess::ThumbsDbAccess(bool)
0131 {
0132     // private constructor, when mutex is locked and
0133     // backend should not be checked
0134 
0135     d->lock.mutex.lock();
0136     d->lock.lockCount++;
0137 }
0138 
0139 ThumbsDb* ThumbsDbAccess::db() const
0140 {
0141     return d->db;
0142 }
0143 
0144 ThumbsDbBackend* ThumbsDbAccess::backend() const
0145 {
0146     return d->backend;
0147 }
0148 
0149 DbEngineParameters ThumbsDbAccess::parameters()
0150 {
0151     if (d)
0152     {
0153         return d->parameters;
0154     }
0155 
0156     return DbEngineParameters();
0157 }
0158 
0159 bool ThumbsDbAccess::isInitialized()
0160 {
0161     return d;
0162 }
0163 
0164 void ThumbsDbAccess::initDbEngineErrorHandler(DbEngineErrorHandler* const errorhandler)
0165 {
0166     if (!d)
0167     {
0168         d = new ThumbsDbAccessStaticPriv();
0169     }
0170 /*
0171     DbEngineErrorHandler* const errorhandler = new DbEngineGuiErrorHandler(d->parameters);
0172 */
0173     d->backend->setDbEngineErrorHandler(errorhandler);
0174 }
0175 
0176 void ThumbsDbAccess::setParameters(const DbEngineParameters& parameters)
0177 {
0178     if (!d)
0179     {
0180         d = new ThumbsDbAccessStaticPriv();
0181     }
0182 
0183     ThumbsDbAccessMutexLocker lock(d);
0184 
0185     if (d->parameters == parameters)
0186     {
0187         return;
0188     }
0189 
0190     if (d->backend && d->backend->isOpen())
0191     {
0192         d->backend->close();
0193     }
0194 
0195     // Kill the old database error handler
0196 
0197     if (d->backend)
0198     {
0199         d->backend->setDbEngineErrorHandler(nullptr);
0200     }
0201 
0202     d->parameters = parameters;
0203 
0204     if (d->parameters.isMySQL() && !d->parameters.internalServer)
0205     {
0206         QFileInfo thumbDB(d->parameters.databaseNameCore);
0207 
0208         if (thumbDB.exists() && thumbDB.isDir() && thumbDB.isAbsolute())
0209         {
0210             d->parameters.databaseType     = QLatin1String("QSQLITE");
0211             d->parameters.databaseNameCore = DbEngineParameters::thumbnailDatabaseFileSQLite(thumbDB.filePath());
0212         }
0213     }
0214 
0215     if (!d->backend || !d->backend->isCompatible(parameters))
0216     {
0217         delete d->db;
0218         delete d->backend;
0219         d->backend = new ThumbsDbBackend(&d->lock);
0220         d->db      = new ThumbsDb(d->backend);
0221     }
0222 }
0223 
0224 bool ThumbsDbAccess::checkReadyForUse(InitializationObserver* const observer)
0225 {
0226     if (!DbEngineAccess::checkReadyForUse(d->lastError))
0227     {
0228         return false;
0229     }
0230 
0231     // Create an object with private shortcut constructor
0232 
0233     ThumbsDbAccess access(false);
0234 
0235     if (!d->backend)
0236     {
0237         qCWarning(DIGIKAM_THUMBSDB_LOG) << "Thumbs database: no database backend available in checkReadyForUse. "
0238                                            "Did you call setParameters before?";
0239         return false;
0240     }
0241 
0242     if (d->backend->isReady())
0243     {
0244         return true;
0245     }
0246 
0247     if (!d->backend->isOpen())
0248     {
0249         if (!d->backend->open(d->parameters))
0250         {
0251             access.setLastError(i18n("Error opening database backend.\n%1",
0252                                 d->backend->lastError()));
0253             return false;
0254         }
0255     }
0256 
0257     // Avoid endless loops (if called methods create new ThumbsDbAccess objects)
0258 
0259     d->initializing = true;
0260 
0261     // Update schema
0262 
0263     ThumbsDbSchemaUpdater updater(&access);
0264     updater.setObserver(observer);
0265 
0266     // Check or set WAL mode for SQLite database from DbEngineParameters
0267 
0268     d->backend->checkOrSetWALMode();
0269 
0270     if (!d->backend->initSchema(&updater))
0271     {
0272         qCWarning(DIGIKAM_THUMBSDB_LOG) << "Thumbs database: cannot process schema initialization";
0273 
0274         d->initializing = false;
0275         return false;
0276     }
0277 
0278     d->initializing = false;
0279 
0280     return d->backend->isReady();
0281 }
0282 
0283 QString ThumbsDbAccess::lastError() const
0284 {
0285     return d->lastError;
0286 }
0287 
0288 void ThumbsDbAccess::setLastError(const QString& error)
0289 {
0290     d->lastError = error;
0291 }
0292 
0293 void ThumbsDbAccess::cleanUpDatabase()
0294 {
0295     if (d)
0296     {
0297         ThumbsDbAccessMutexLocker locker(d);
0298 
0299         if (d->backend)
0300         {
0301             d->backend->close();
0302             delete d->db;
0303             delete d->backend;
0304         }
0305     }
0306 
0307     delete d;
0308     d = nullptr;
0309 }
0310 
0311 } // namespace Digikam