File indexing completed on 2025-01-05 03:54:13

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