File indexing completed on 2025-03-09 03:54:59

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2007-03-18
0007  * Description : Face 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 "facedbaccess.h"
0017 
0018 // Qt includes
0019 
0020 #include <QMutex>
0021 #include <QMutexLocker>
0022 #include <QSqlDatabase>
0023 
0024 // KDE includes
0025 
0026 #include <klocalizedstring.h>
0027 
0028 // Local includes
0029 
0030 #include "digikam_debug.h"
0031 #include "facedbbackend.h"
0032 #include "facedb.h"
0033 #include "facedbschemaupdater.h"
0034 #include "dbengineaccess.h"
0035 #include "dbengineerrorhandler.h"
0036 
0037 namespace Digikam
0038 {
0039 
0040 class Q_DECL_HIDDEN FaceDbAccessStaticPriv
0041 {
0042 public:
0043 
0044     explicit FaceDbAccessStaticPriv()
0045         : backend     (nullptr),
0046           db          (nullptr),
0047           initializing(false)
0048     {
0049     }
0050 
0051     ~FaceDbAccessStaticPriv()
0052     {
0053     }
0054 
0055 public:
0056 
0057     FaceDbBackend*     backend;
0058     FaceDb*            db;
0059     DbEngineParameters parameters;
0060     DbEngineLocking    lock;
0061     QString            lastError;
0062     bool               initializing;
0063 };
0064 
0065 FaceDbAccessStaticPriv* FaceDbAccess::d = nullptr;
0066 
0067 // -----------------------------------------------------------------------------
0068 
0069 class Q_DECL_HIDDEN FaceDbAccessMutexLocker
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 FaceDbAccessMutexLocker(FaceDbAccessStaticPriv* const dd)
0085         : QMutexLocker(&dd->lock.mutex),
0086           d           (dd)
0087     {
0088         d->lock.lockCount++;
0089     }
0090 
0091     ~FaceDbAccessMutexLocker()
0092     {
0093         d->lock.lockCount--;
0094     }
0095 
0096 public:
0097 
0098     FaceDbAccessStaticPriv* const d;
0099 };
0100 
0101 // -----------------------------------------------------------------------------
0102 
0103 FaceDbAccess::FaceDbAccess()
0104 {
0105     // You will want to call setParameters before constructing FaceDbAccess.
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         d->backend->open(d->parameters);
0118         d->initializing = false;
0119     }
0120 }
0121 
0122 FaceDbAccess::~FaceDbAccess()
0123 {
0124     d->lock.lockCount--;
0125     d->lock.mutex.unlock();
0126 }
0127 
0128 FaceDbAccess::FaceDbAccess(bool)
0129 {
0130     // private constructor, when mutex is locked and
0131     // backend should not be checked
0132 
0133     d->lock.mutex.lock();
0134     d->lock.lockCount++;
0135 }
0136 
0137 FaceDb* FaceDbAccess::db() const
0138 {
0139     return d->db;
0140 }
0141 
0142 FaceDbBackend* FaceDbAccess::backend() const
0143 {
0144     return d->backend;
0145 }
0146 
0147 DbEngineParameters FaceDbAccess::parameters()
0148 {
0149     if (d)
0150     {
0151         return d->parameters;
0152     }
0153 
0154     return DbEngineParameters();
0155 }
0156 
0157 bool FaceDbAccess::isInitialized()
0158 {
0159     return d;
0160 }
0161 
0162 void FaceDbAccess::initDbEngineErrorHandler(DbEngineErrorHandler* const errorhandler)
0163 {
0164     if (!d)
0165     {
0166         d = new FaceDbAccessStaticPriv();
0167     }
0168 /*
0169     DbEngineErrorHandler* const errorhandler = new DbEngineGuiErrorHandler(d->parameters);
0170 */
0171     d->backend->setDbEngineErrorHandler(errorhandler);
0172 }
0173 
0174 void FaceDbAccess::setParameters(const DbEngineParameters& parameters)
0175 {
0176     if (!d)
0177     {
0178         d = new FaceDbAccessStaticPriv();
0179     }
0180 
0181     FaceDbAccessMutexLocker lock(d);
0182 
0183     if (d->parameters == parameters)
0184     {
0185         return;
0186     }
0187 
0188     if (d->backend && d->backend->isOpen())
0189     {
0190         d->backend->close();
0191     }
0192 
0193     // Kill the old database error handler
0194 
0195     if (d->backend)
0196     {
0197         d->backend->setDbEngineErrorHandler(nullptr);
0198     }
0199 
0200     d->parameters = parameters;
0201 
0202     if (!d->backend || !d->backend->isCompatible(parameters))
0203     {
0204         delete d->db;
0205         delete d->backend;
0206         d->backend = new FaceDbBackend(&d->lock);
0207         d->db      = new FaceDb(d->backend);
0208     }
0209 }
0210 
0211 bool FaceDbAccess::checkReadyForUse(InitializationObserver* const observer)
0212 {
0213     if (!DbEngineAccess::checkReadyForUse(d->lastError))
0214     {
0215         return false;
0216     }
0217 
0218     // Create an object with private shortcut constructor
0219 
0220     FaceDbAccess access(false);
0221 
0222     if (!d->backend)
0223     {
0224         qCWarning(DIGIKAM_FACEDB_LOG) << "Face database: no database backend available in checkReadyForUse. "
0225                                          "Did you call setParameters before?";
0226         return false;
0227     }
0228 
0229     if (d->backend->isReady())
0230     {
0231         return true;
0232     }
0233 
0234     if (!d->backend->isOpen())
0235     {
0236         if (!d->backend->open(d->parameters))
0237         {
0238             access.setLastError(i18n("Error opening database backend.\n%1",
0239                                      d->backend->lastError()));
0240             return false;
0241         }
0242     }
0243 
0244     // Avoid endless loops (if called methods create new FaceDbAccess objects)
0245 
0246     d->initializing = true;
0247 
0248     // Update schema
0249 
0250     FaceDbSchemaUpdater updater(&access);
0251     updater.setObserver(observer);
0252 
0253     // Check or set WAL mode for SQLite database from DbEngineParameters
0254 
0255     d->backend->checkOrSetWALMode();
0256 
0257     if (!d->backend->initSchema(&updater))
0258     {
0259         qCWarning(DIGIKAM_FACEDB_LOG) << "Face database: cannot process schema initialization";
0260 
0261         d->initializing = false;
0262         return false;
0263     }
0264 
0265     d->initializing = false;
0266 
0267     return d->backend->isReady();
0268 }
0269 
0270 QString FaceDbAccess::lastError() const
0271 {
0272     return d->lastError;
0273 }
0274 
0275 void FaceDbAccess::setLastError(const QString& error)
0276 {
0277     d->lastError = error;
0278 }
0279 
0280 void FaceDbAccess::cleanUpDatabase()
0281 {
0282     if (d)
0283     {
0284         FaceDbAccessMutexLocker locker(d);
0285 
0286         if (d->backend)
0287         {
0288             d->backend->close();
0289             delete d->db;
0290             delete d->backend;
0291         }
0292     }
0293 
0294     delete d;
0295     d = nullptr;
0296 }
0297 
0298 // ---------------------------------------------------------------------------------
0299 
0300 FaceDbAccessUnlock::FaceDbAccessUnlock()
0301 {
0302     // acquire lock
0303 
0304     FaceDbAccess::d->lock.mutex.lock();
0305 
0306     // store lock count
0307 
0308     count = FaceDbAccess::d->lock.lockCount;
0309 
0310     // set lock count to 0
0311 
0312     FaceDbAccess::d->lock.lockCount = 0;
0313 
0314     // unlock
0315 
0316     for (int i = 0 ; i < count ; ++i)
0317     {
0318         FaceDbAccess::d->lock.mutex.unlock();
0319     }
0320 
0321     // drop lock acquired in first line. Mutex is now free.
0322 
0323     FaceDbAccess::d->lock.mutex.unlock();
0324 }
0325 
0326 FaceDbAccessUnlock::FaceDbAccessUnlock(FaceDbAccess* const)
0327 {
0328     // With the passed pointer, we have assured that the mutex is acquired
0329     // Store lock count
0330 
0331     count = FaceDbAccess::d->lock.lockCount;
0332 
0333     // set lock count to 0
0334 
0335     FaceDbAccess::d->lock.lockCount = 0;
0336 
0337     // unlock
0338 
0339     for (int i = 0 ; i < count ; ++i)
0340     {
0341         FaceDbAccess::d->lock.mutex.unlock();
0342     }
0343 
0344     // Mutex is now free
0345 }
0346 
0347 FaceDbAccessUnlock::~FaceDbAccessUnlock()
0348 {
0349     // lock as often as it was locked before
0350 
0351     for (int i = 0 ; i < count ; ++i)
0352     {
0353         FaceDbAccess::d->lock.mutex.lock();
0354     }
0355 
0356     // update lock count
0357 
0358     FaceDbAccess::d->lock.lockCount += count;
0359 }
0360 
0361 } // namespace Digikam