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 : 2009-05-29 0007 * Description : Thumbnails database interface. 0008 * 0009 * SPDX-FileCopyrightText: 2009 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de> 0010 * SPDX-FileCopyrightText: 2009-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 "thumbsdb.h" 0017 0018 // Qt includes 0019 0020 #include <QMap> 0021 0022 // Local includes 0023 0024 #include "digikam_debug.h" 0025 0026 namespace Digikam 0027 { 0028 0029 class Q_DECL_HIDDEN ThumbsDb::Private 0030 { 0031 0032 public: 0033 0034 explicit Private() 0035 : db(nullptr) 0036 { 0037 } 0038 0039 ThumbsDbBackend* db; 0040 }; 0041 0042 ThumbsDb::ThumbsDb(ThumbsDbBackend* const backend) 0043 : d(new Private) 0044 { 0045 d->db = backend; 0046 } 0047 0048 ThumbsDb::~ThumbsDb() 0049 { 0050 delete d; 0051 } 0052 0053 bool ThumbsDb::setSetting(const QString& keyword, const QString& value ) 0054 { 0055 QMap<QString, QVariant> parameters; 0056 parameters.insert(QLatin1String(":keyword"), keyword); 0057 parameters.insert(QLatin1String(":value"), value); 0058 BdEngineBackend::QueryState queryStateResult = d->db->execDBAction(d->db->getDBAction(QLatin1String("ReplaceThumbnailSetting")), 0059 parameters); 0060 0061 return (queryStateResult == BdEngineBackend::NoErrors); 0062 } 0063 0064 QString ThumbsDb::getSetting(const QString& keyword) 0065 { 0066 QMap<QString, QVariant> parameters; 0067 parameters.insert(QLatin1String(":keyword"), keyword); 0068 QList<QVariant> values; 0069 0070 // TODO Should really check return status here 0071 0072 BdEngineBackend::QueryState queryStateResult = d->db->execDBAction(d->db->getDBAction(QLatin1String("SelectThumbnailSetting")), 0073 parameters, &values); 0074 0075 qCDebug(DIGIKAM_THUMBSDB_LOG) << "ThumbDB SelectThumbnailSetting val ret = " 0076 << (BdEngineBackend::QueryStateEnum)queryStateResult; 0077 0078 if (!values.isEmpty()) 0079 { 0080 return values.first().toString(); 0081 } 0082 0083 return QString(); 0084 } 0085 0086 QString ThumbsDb::getLegacySetting(const QString& keyword) 0087 { 0088 QMap<QString, QVariant> parameters; 0089 parameters.insert(QLatin1String(":keyword"), keyword); 0090 QList<QVariant> values; 0091 0092 // TODO Should really check return status here 0093 0094 BdEngineBackend::QueryState queryStateResult = d->db->execDBAction(d->db->getDBAction(QLatin1String("SelectThumbnailLegacySetting")), 0095 parameters, &values); 0096 0097 qCDebug(DIGIKAM_THUMBSDB_LOG) << "ThumbDB SelectThumbnailLegacySetting val ret = " 0098 << (BdEngineBackend::QueryStateEnum)queryStateResult; 0099 0100 if (!values.isEmpty()) 0101 { 0102 return values.first().toString(); 0103 } 0104 0105 return QString(); 0106 } 0107 0108 ThumbsDbInfo ThumbsDb::fillThumbnailInfo(const QList<QVariant>& values) 0109 { 0110 if (values.isEmpty()) 0111 { 0112 return ThumbsDbInfo(); 0113 } 0114 0115 ThumbsDbInfo info; 0116 0117 info.id = values.at(0).toInt(); 0118 info.type = (DatabaseThumbnail::Type)values.at(1).toInt(); 0119 info.modificationDate = values.at(2).toDateTime(); 0120 info.modificationDate.setTimeSpec(Qt::UTC); 0121 info.orientationHint = values.at(3).toInt(); 0122 info.data = values.at(4).toByteArray(); 0123 0124 return info; 0125 } 0126 0127 ThumbsDbInfo ThumbsDb::findByHash(const QString& uniqueHash, qlonglong fileSize) 0128 { 0129 QList<QVariant> values; 0130 d->db->execSql(QLatin1String("SELECT id, type, modificationDate, orientationHint, data " 0131 "FROM Thumbnails " 0132 " INNER JOIN UniqueHashes ON id = thumbId " 0133 " WHERE uniqueHash=? AND fileSize=?;"), 0134 uniqueHash, fileSize, &values); 0135 0136 return fillThumbnailInfo(values); 0137 } 0138 0139 ThumbsDbInfo ThumbsDb::findByFilePath(const QString& path) 0140 { 0141 QList<QVariant> values; 0142 d->db->execSql(QLatin1String("SELECT id, type, modificationDate, orientationHint, data " 0143 "FROM Thumbnails " 0144 " INNER JOIN FilePaths ON id = thumbId " 0145 " WHERE path=?;"), 0146 path, &values); 0147 0148 return fillThumbnailInfo(values); 0149 } 0150 0151 ThumbsDbInfo ThumbsDb::findByFilePath(const QString& path, const QString& uniqueHash) 0152 { 0153 ThumbsDbInfo info = findByFilePath(path); 0154 0155 if (uniqueHash.isNull()) 0156 { 0157 return info; 0158 } 0159 0160 if (info.data.isNull()) 0161 { 0162 return info; 0163 } 0164 0165 // double check that thumbnail is not referenced by a different hash 0166 0167 QList<QVariant> values; 0168 d->db->execSql(QLatin1String("SELECT uniqueHash FROM UniqueHashes WHERE thumbId=?;"), 0169 info.id, &values); 0170 0171 if (values.isEmpty()) 0172 { 0173 return info; 0174 } 0175 0176 Q_FOREACH (const QVariant& hash, values) 0177 { 0178 if (hash == uniqueHash) 0179 { // cppcheck-suppress useStlAlgorithm 0180 return info; 0181 } 0182 } 0183 0184 return ThumbsDbInfo(); 0185 } 0186 0187 ThumbsDbInfo ThumbsDb::findByCustomIdentifier(const QString& id) 0188 { 0189 QList<QVariant> values; 0190 d->db->execSql(QLatin1String("SELECT id, type, modificationDate, orientationHint, data " 0191 "FROM Thumbnails " 0192 " INNER JOIN CustomIdentifiers ON id = thumbId " 0193 " WHERE identifier=?;"), 0194 id, &values); 0195 0196 return fillThumbnailInfo(values); 0197 } 0198 0199 QList<int> ThumbsDb::findAll() 0200 { 0201 QList<QVariant> values; 0202 d->db->execSql(QLatin1String("SELECT id FROM Thumbnails;"), 0203 &values); 0204 0205 QList<int> thumbIds; 0206 0207 Q_FOREACH (const QVariant& object, values) 0208 { 0209 thumbIds << object.toInt(); 0210 } 0211 0212 return thumbIds; 0213 } 0214 0215 QHash<QString, int> ThumbsDb::getFilePathsWithThumbnail() 0216 { 0217 DbEngineSqlQuery query = d->db->prepareQuery(QString::fromLatin1("SELECT path, thumbId " 0218 "FROM FilePaths " 0219 " INNER JOIN Thumbnails ON thumbId = id " 0220 " WHERE type BETWEEN %1 AND %2;") 0221 .arg(DatabaseThumbnail::PGF) 0222 .arg(DatabaseThumbnail::PNG)); 0223 0224 if (!d->db->exec(query)) 0225 { 0226 return QHash<QString, int>(); 0227 } 0228 0229 QHash <QString, int> filePaths; 0230 0231 while (query.next()) 0232 { 0233 filePaths[query.value(0).toString()] = query.value(1).toInt(); 0234 } 0235 0236 return filePaths; 0237 } 0238 0239 BdEngineBackend::QueryState ThumbsDb::insertUniqueHash(const QString& uniqueHash, qlonglong fileSize, int thumbId) 0240 { 0241 return d->db->execSql(QLatin1String("REPLACE INTO UniqueHashes (uniqueHash, fileSize, thumbId) VALUES (?,?,?);"), 0242 uniqueHash, fileSize, thumbId); 0243 } 0244 0245 BdEngineBackend::QueryState ThumbsDb::insertFilePath(const QString& path, int thumbId) 0246 { 0247 return d->db->execSql(QLatin1String("REPLACE INTO FilePaths (path, thumbId) VALUES (?,?);"), 0248 path, thumbId); 0249 } 0250 0251 BdEngineBackend::QueryState ThumbsDb::insertCustomIdentifier(const QString& path, int thumbId) 0252 { 0253 return d->db->execSql(QLatin1String("REPLACE INTO CustomIdentifiers (identifier, thumbId) VALUES (?,?);"), 0254 path, thumbId); 0255 } 0256 0257 BdEngineBackend::QueryState ThumbsDb::remove(int thumbId) 0258 { 0259 return d->db->execSql(QLatin1String("DELETE FROM Thumbnails WHERE id=?;"), thumbId); 0260 } 0261 0262 BdEngineBackend::QueryState ThumbsDb::removeByUniqueHash(const QString& uniqueHash, qlonglong fileSize) 0263 { 0264 // UniqueHashes + FilePaths entries are removed by trigger 0265 0266 QMap<QString, QVariant> parameters; 0267 parameters.insert(QLatin1String(":uniqueHash"), uniqueHash); 0268 parameters.insert(QLatin1String(":filesize"), fileSize); 0269 0270 return d->db->execDBAction(d->db->getDBAction(QLatin1String("Delete_Thumbnail_ByUniqueHashId")), 0271 parameters); 0272 } 0273 0274 BdEngineBackend::QueryState ThumbsDb::removeByFilePath(const QString& path) 0275 { 0276 // UniqueHashes + FilePaths entries are removed by trigger 0277 0278 QMap<QString, QVariant> parameters; 0279 parameters.insert(QLatin1String(":path"), path); 0280 0281 return d->db->execDBAction(d->db->getDBAction(QLatin1String("Delete_Thumbnail_ByPath")), 0282 parameters); 0283 } 0284 0285 BdEngineBackend::QueryState ThumbsDb::removeByCustomIdentifier(const QString& id) 0286 { 0287 // UniqueHashes + FilePaths entries are removed by trigger 0288 0289 QMap<QString, QVariant> parameters; 0290 parameters.insert(QLatin1String(":identifier"), id); 0291 0292 return d->db->execDBAction(d->db->getDBAction(QLatin1String("Delete_Thumbnail_ByCustomIdentifier")), 0293 parameters); 0294 } 0295 0296 BdEngineBackend::QueryState ThumbsDb::insertThumbnail(const ThumbsDbInfo& info, QVariant* const lastInsertId) 0297 { 0298 QVariant id; 0299 BdEngineBackend::QueryState lastQueryState; 0300 lastQueryState = d->db->execSql(QLatin1String("INSERT INTO Thumbnails (type, modificationDate, orientationHint, data) VALUES (?, ?, ?, ?);"), 0301 info.type, info.modificationDate, info.orientationHint, info.data, nullptr, &id); 0302 0303 if (BdEngineBackend::NoErrors == lastQueryState) 0304 { 0305 *lastInsertId = id.toInt(); 0306 } 0307 else 0308 { 0309 *lastInsertId = -1; 0310 } 0311 0312 return lastQueryState; 0313 } 0314 0315 BdEngineBackend::QueryState ThumbsDb::renameByFilePath(const QString& oldPath, const QString& newPath) 0316 { 0317 return d->db->execSql(QLatin1String("UPDATE FilePaths SET path=? WHERE path=?;"), 0318 newPath, oldPath); 0319 } 0320 0321 BdEngineBackend::QueryState ThumbsDb::replaceThumbnail(const ThumbsDbInfo& info) 0322 { 0323 return d->db->execSql(QLatin1String("REPLACE INTO Thumbnails (id, type, modificationDate, orientationHint, data) VALUES(?, ?, ?, ?, ?);"), 0324 QList<QVariant>() << info.id << info.type << info.modificationDate << info.orientationHint << info.data); 0325 } 0326 0327 BdEngineBackend::QueryState ThumbsDb::updateModificationDate(int thumbId, const QDateTime& modificationDate) 0328 { 0329 return d->db->execSql(QLatin1String("UPDATE Thumbnails SET modificationDate=? WHERE id=?;"), 0330 modificationDate, thumbId); 0331 } 0332 0333 void ThumbsDb::replaceUniqueHash(const QString& oldUniqueHash, int oldFileSize, 0334 const QString& newUniqueHash, int newFileSize) 0335 { 0336 d->db->execSql(QLatin1String("UPDATE UniqueHashes SET uniqueHash=?, fileSize=? WHERE uniqueHash=? AND fileSize=?;"), 0337 newUniqueHash, newFileSize, oldUniqueHash, oldFileSize); 0338 } 0339 0340 bool ThumbsDb::integrityCheck() 0341 { 0342 QList<QVariant> values; 0343 d->db->execDBAction(d->db->getDBAction(QString::fromUtf8("checkThumbnailsDbIntegrity")), 0344 &values); 0345 0346 switch (d->db->databaseType()) 0347 { 0348 case BdEngineBackend::DbType::SQLite: 0349 0350 // For SQLite the integrity check returns a single row with one string column "ok" on success and multiple rows on error. 0351 0352 return ((values.size() == 1) && (values.first().toString().toLower().compare(QLatin1String("ok")) == 0)); 0353 0354 case BdEngineBackend::DbType::MySQL: 0355 0356 // For MySQL, for every checked table, the table name, operation (check), message type (status) and the message text (ok on success) 0357 // are returned. So we check if there are four elements and if yes, whether the fourth element is "ok". 0358 /* 0359 qCDebug(DIGIKAM_DATABASE_LOG) << "MySQL check returned " << values.size() << " rows"; 0360 */ 0361 if ((values.size() % 4) != 0) 0362 { 0363 return false; 0364 } 0365 0366 for (QList<QVariant>::iterator it = values.begin() ; it != values.end() ; ) 0367 { 0368 QString tableName = (*it).toString(); 0369 ++it; 0370 QString operation = (*it).toString(); 0371 ++it; 0372 QString messageType = (*it).toString(); 0373 ++it; 0374 QString messageText = (*it).toString(); 0375 ++it; 0376 0377 if (messageText.toLower().compare(QLatin1String("ok")) != 0) 0378 { 0379 qCDebug(DIGIKAM_DATABASE_LOG) << "Failed integrity check for table " << tableName 0380 << ". Reason:" << messageText; 0381 return false; 0382 } 0383 else 0384 { 0385 /* 0386 qCDebug(DIGIKAM_DATABASE_LOG) << "Passed integrity check for table " << tableName; 0387 */ 0388 } 0389 } 0390 0391 // No error conditions. Db passed the integrity check. 0392 0393 return true; 0394 0395 default: 0396 { 0397 return false; 0398 } 0399 } 0400 } 0401 0402 void ThumbsDb::vacuum() 0403 { 0404 d->db->execDBAction(d->db->getDBAction(QString::fromUtf8("vacuumThumbnailsDB"))); 0405 } 0406 0407 } // namespace Digikam