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