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-04-16 0007 * Description : Face database schema updater 0008 * 0009 * SPDX-FileCopyrightText: 2007-2009 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: 2020 by Nghia Duong <minhnghiaduong997 at gmail dot com> 0012 * 0013 * SPDX-License-Identifier: GPL-2.0-or-later 0014 * 0015 * ============================================================ */ 0016 0017 #include "facedbschemaupdater.h" 0018 0019 // KDE includes 0020 0021 #include <klocalizedstring.h> 0022 0023 // Local includes 0024 0025 #include "digikam_debug.h" 0026 #include "dbenginebackend.h" 0027 #include "facedbaccess.h" 0028 #include "facedb.h" 0029 0030 namespace Digikam 0031 { 0032 0033 int FaceDbSchemaUpdater::schemaVersion() 0034 { 0035 return 4; 0036 } 0037 0038 // ------------------------------------------------------------------------------------- 0039 0040 class Q_DECL_HIDDEN FaceDbSchemaUpdater::Private 0041 { 0042 public: 0043 0044 explicit Private() 0045 : setError (false), 0046 currentVersion (0), 0047 currentRequiredVersion(0), 0048 dbAccess (nullptr), 0049 observer (nullptr) 0050 { 0051 } 0052 0053 bool setError; 0054 0055 int currentVersion; 0056 int currentRequiredVersion; 0057 0058 FaceDbAccess* dbAccess; 0059 0060 InitializationObserver* observer; 0061 }; 0062 0063 FaceDbSchemaUpdater::FaceDbSchemaUpdater(FaceDbAccess* const dbAccess) 0064 : d(new Private) 0065 { 0066 d->dbAccess = dbAccess; 0067 } 0068 0069 FaceDbSchemaUpdater::~FaceDbSchemaUpdater() 0070 { 0071 delete d; 0072 } 0073 0074 void FaceDbSchemaUpdater::setObserver(InitializationObserver* const observer) 0075 { 0076 d->observer = observer; 0077 } 0078 0079 bool FaceDbSchemaUpdater::update() 0080 { 0081 bool success = startUpdates(); 0082 0083 // even on failure, try to set current version - it may have incremented 0084 0085 if (d->currentVersion) 0086 { 0087 d->dbAccess->db()->setSetting(QLatin1String("DBFaceVersion"), QString::number(d->currentVersion)); 0088 } 0089 0090 if (d->currentRequiredVersion) 0091 { 0092 d->dbAccess->db()->setSetting(QLatin1String("DBFaceVersionRequired"), QString::number(d->currentRequiredVersion)); 0093 } 0094 0095 return success; 0096 } 0097 0098 bool FaceDbSchemaUpdater::startUpdates() 0099 { 0100 // First step: do we have an empty database? 0101 0102 QStringList tables = d->dbAccess->backend()->tables(); 0103 0104 if (tables.contains(QLatin1String("Identities"), Qt::CaseInsensitive)) 0105 { 0106 // Find out schema version of db file 0107 0108 QString version = d->dbAccess->db()->setting(QLatin1String("DBFaceVersion")); 0109 QString versionRequired = d->dbAccess->db()->setting(QLatin1String("DBFaceVersionRequired")); 0110 qCDebug(DIGIKAM_FACEDB_LOG) << "Face database: have a structure version " << version; 0111 0112 // mini schema update 0113 0114 if (version.isEmpty() && d->dbAccess->parameters().isSQLite()) 0115 { 0116 version = d->dbAccess->db()->setting(QLatin1String("DBVersion")); 0117 } 0118 0119 // We absolutely require the DBFaceVersion setting 0120 0121 if (version.isEmpty()) 0122 { 0123 // Something is damaged. Give up. 0124 0125 qCWarning(DIGIKAM_FACEDB_LOG) << "DBFaceVersion not available! Giving up schema upgrading."; 0126 0127 QString errorMsg = i18n("The database is not valid: " 0128 "the \"DBFaceVersion\" setting does not exist. " 0129 "The current database schema version cannot be verified. " 0130 "Try to start with an empty database. "); 0131 0132 d->dbAccess->setLastError(errorMsg); 0133 0134 if (d->observer) 0135 { 0136 d->observer->error(errorMsg); 0137 d->observer->finishedSchemaUpdate(InitializationObserver::UpdateErrorMustAbort); 0138 } 0139 0140 return false; 0141 } 0142 0143 // current version describes the current state of the schema in the db, 0144 // schemaVersion is the version required by the program. 0145 0146 d->currentVersion = version.toInt(); 0147 0148 if (d->currentVersion > schemaVersion()) 0149 { 0150 // trying to open a database with a more advanced than this FaceDbSchemaUpdater supports 0151 0152 if (!versionRequired.isEmpty() && (versionRequired.toInt() <= schemaVersion())) 0153 { 0154 // version required may be less than current version 0155 0156 return true; 0157 } 0158 else 0159 { 0160 QString errorMsg = i18n("The database has been used with a more recent version of digiKam " 0161 "and has been updated to a database schema which cannot be used with this version. " 0162 "(This means this digiKam version is too old, or the database format is to recent.) " 0163 "Please use the more recent version of digiKam that you used before."); 0164 0165 d->dbAccess->setLastError(errorMsg); 0166 0167 if (d->observer) 0168 { 0169 d->observer->error(errorMsg); 0170 d->observer->finishedSchemaUpdate(InitializationObserver::UpdateErrorMustAbort); 0171 } 0172 0173 return false; 0174 } 0175 } 0176 else 0177 { 0178 return makeUpdates(); 0179 } 0180 } 0181 else 0182 { 0183 qCDebug(DIGIKAM_FACEDB_LOG) << "Face database: no database file available"; 0184 0185 DbEngineParameters parameters = d->dbAccess->parameters(); 0186 0187 // No legacy handling: start with a fresh db 0188 0189 if (!createDatabase()) 0190 { 0191 QString errorMsg = i18n("Failed to create tables in database.\n%1", 0192 d->dbAccess->backend()->lastError()); 0193 d->dbAccess->setLastError(errorMsg); 0194 0195 if (d->observer) 0196 { 0197 d->observer->error(errorMsg); 0198 d->observer->finishedSchemaUpdate(InitializationObserver::UpdateErrorMustAbort); 0199 } 0200 0201 return false; 0202 } 0203 0204 return true; 0205 } 0206 } 0207 0208 bool FaceDbSchemaUpdater::makeUpdates() 0209 { 0210 if (d->currentVersion < schemaVersion()) 0211 { 0212 if (d->currentVersion == 1) 0213 { 0214 updateV1ToV2(); 0215 } 0216 else if (d->currentVersion == 2) 0217 { 0218 updateV2ToV3(); 0219 } 0220 else if (d->currentVersion == 3) 0221 { 0222 updateV3ToV4(); 0223 } 0224 } 0225 0226 return true; 0227 } 0228 0229 0230 bool FaceDbSchemaUpdater::createDatabase() 0231 { 0232 if (createTables() && createIndices() && createTriggers()) 0233 { 0234 d->currentVersion = schemaVersion(); 0235 d->currentRequiredVersion = 4; 0236 return true; 0237 } 0238 else 0239 { 0240 return false; 0241 } 0242 } 0243 0244 bool FaceDbSchemaUpdater::createTables() 0245 { 0246 // the creation order is important because of the foreign keys in MySQL 0247 0248 return ( 0249 d->dbAccess->backend()->execDBAction(d->dbAccess->backend()->getDBAction(QLatin1String("CreateFaceDB"))) && 0250 d->dbAccess->backend()->execDBAction(d->dbAccess->backend()->getDBAction(QLatin1String("CreateFaceDBFaceMatrices"))) && 0251 d->dbAccess->backend()->execDBAction(d->dbAccess->backend()->getDBAction(QLatin1String("CreateFaceDBKDTree"))) 0252 ); 0253 } 0254 0255 bool FaceDbSchemaUpdater::createIndices() 0256 { 0257 return d->dbAccess->backend()->execDBAction(d->dbAccess->backend()->getDBAction(QLatin1String("CreateFaceIndices"))); 0258 } 0259 0260 bool FaceDbSchemaUpdater::createTriggers() 0261 { 0262 return d->dbAccess->backend()->execDBAction(d->dbAccess->backend()->getDBAction(QLatin1String("CreateFaceTriggers"))); 0263 } 0264 0265 bool FaceDbSchemaUpdater::updateV1ToV2() 0266 { 0267 /* 0268 if (!d->dbAccess->backend()->execDBAction(d->dbAccess->backend()->getDBAction("UpdateDBSchemaFromV1ToV2"))) 0269 { 0270 qError() << "Schema upgrade in DB from V1 to V2 failed!"; 0271 return false; 0272 } 0273 */ 0274 0275 d->currentVersion = 2; 0276 d->currentRequiredVersion = 1; 0277 0278 return true; 0279 } 0280 0281 bool FaceDbSchemaUpdater::updateV2ToV3() 0282 { 0283 d->currentVersion = 3; 0284 d->currentRequiredVersion = 3; 0285 d->dbAccess->backend()->execDBAction(d->dbAccess->backend()->getDBAction(QLatin1String("CreateFaceDBFaceMatrices"))); 0286 0287 return true; 0288 } 0289 0290 bool FaceDbSchemaUpdater::updateV3ToV4() 0291 { 0292 if (!(d->dbAccess->backend()->execDBAction(d->dbAccess->backend()->getDBAction(QLatin1String("CreateFaceDBFaceMatrices"))))) 0293 { 0294 qCDebug(DIGIKAM_FACEDB_LOG) << "fail to recreate FaceMatrices table"; 0295 0296 return false; 0297 } 0298 0299 if (!(d->dbAccess->backend()->execDBAction(d->dbAccess->backend()->getDBAction(QLatin1String("CreateFaceDBKDTree"))))) 0300 { 0301 qCDebug(DIGIKAM_FACEDB_LOG) << "fail to create KDTree table"; 0302 0303 return false; 0304 } 0305 0306 d->currentVersion = 4; 0307 d->currentRequiredVersion = 4; 0308 0309 // TODO: retrain recognized identities 0310 0311 return true; 0312 } 0313 0314 } // namespace Digikam