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