Krita Source Code Documentation
Loading...
Searching...
No Matches
KisResourceCacheDb Class Reference

The KisResourceCacheDb class encapsulates the database that caches information about the resources available to the user. More...

#include <KisResourceCacheDb.h>

Static Public Member Functions

static void deleteTemporaryResources ()
 Delete all storages that are Unknown or Memory and all resources that are marked temporary or belong to Unknown or Memory storages.
 
static bool getForeignKeysStateImpl ()
 
static bool initialize (const QString &location)
 initializes the database and updates the scheme if necessary. Does not actually fill the database with pointers to resources.
 
static bool isValid ()
 isValid
 
static QString lastError ()
 lastError returns the last SQL error.
 
static void performHouseKeepingOnExit ()
 perform optimize and vacuum when necessary
 
static void setForeignKeysStateImpl (bool isEnabled)
 
static void synchronizeForeignKeysState ()
 

Static Public Attributes

static const QString databaseVersion { "0.0.18" }
 current schema version
 
static QStringList disabledBundles { QStringList() << "Krita_3_Default_Resources.bundle" }
 the list of compatibility bundles that need to inactive by default
 
static const QString resourceCacheDbFilename { "resourcecache.sqlite" }
 filename of the database
 
static QStringList storageTypes { QStringList() }
 kinds of places where resources can be stored
 

Private Member Functions

 KisResourceCacheDb ()
 
KisResourceCacheDb operator= (const KisResourceCacheDb &)
 
 ~KisResourceCacheDb ()
 

Static Private Member Functions

static bool addMetaDataForId (const QMap< QString, QVariant > map, int id, const QString &tableName)
 
static bool addResource (KisResourceStorageSP storage, QDateTime timestamp, KoResourceSP resource, const QString &resourceType)
 
static bool addResources (KisResourceStorageSP storage, QString resourceType)
 
static bool addResourceVersion (int resourceId, QDateTime timestamp, KisResourceStorageSP storage, KoResourceSP resource)
 addResourceVersion adds a new version of the resource to the database. The resource itself already should be updated with the updated filename and version.
 
static bool addResourceVersionImpl (int resourceId, QDateTime timestamp, KisResourceStorageSP storage, KoResourceSP resource)
 
static bool addStorage (KisResourceStorageSP storage, bool preinstalled)
 
static bool addStorageTags (KisResourceStorageSP storage)
 
static bool addTag (const QString &resourceType, const QString storageLocation, KisTagSP tag)
 
static bool addTags (KisResourceStorageSP storage, QString resourceType)
 
static bool deleteStorage (KisResourceStorageSP storage)
 Actually delete the storage and all its resources from the database (i.e., nothing is set to inactive, it's deleted)
 
static bool deleteStorage (QString location)
 
static bool getAllVersionsLocations (int resourceId, QStringList &outVersionsLocationsList)
 
static bool getResourceIdFromFilename (QString filename, QString resourceType, QString storageLocation, int &outResourceId)
 The function will find the resource only if it is the latest version.
 
static bool getResourceIdFromVersionedFilename (QString filename, QString resourceType, QString storageLocation, int &outResourceId)
 Note that here you can put even the original filename - any filename from the versioned_resources - and it will still find it.
 
static bool hasTag (const QString &url, const QString &resourceType)
 
static bool linkTagToStorage (const QString &url, const QString &resourceType, const QString &storageLocation)
 
static bool makeResourceTheCurrentVersion (int resourceId, KoResourceSP resource)
 
static QMap< QString, QVariant > metaDataForId (int id, const QString &tableName)
 metaDataForId
 
static bool registerResourceType (const QString &resourceType)
 registerResourceType registers this resource type in the database
 
static bool registerStorageType (const KisResourceStorage::StorageType storageType)
 registerStorageType registers this storage type in the database
 
static bool removeOrphanedMetaData ()
 removeOrphanedMetaData Previous versions of Krita never removed metadata, so this function doublechecks and removes any orphaned metadata for either storages or resources from the database.
 
static bool removeResourceCompletely (int resourceId)
 
static bool removeResourceVersionImpl (int resourceId, int version, KisResourceStorageSP storage)
 
static int resourceIdForResource (const QString &resourceFileName, const QString &resourceType, const QString &storageLocation)
 
static bool resourceNeedsUpdating (int resourceId, QDateTime timestamp)
 
static QVector< int > resourcesForStorage (const QString &resourceType, const QString &storageLocation)
 
static bool setResourceActive (int resourceId, bool active=false)
 Make this resource active or inactive; this does not remove the resource from disk or from the database.
 
static bool synchronizeStorage (KisResourceStorageSP storage)
 
static bool tagResource (const QString &resourceFileName, KisTagSP tag, const QString &resourceType)
 
static bool updateMetaDataForId (const QMap< QString, QVariant > map, int id, const QString &tableName)
 setMetaDataForId removes all metadata for the given id and table name, and inserts the metadata in the metadata table.
 
static bool updateResourceTableForResourceIfNeeded (int resourceId, const QString &resourceType, KisResourceStorageSP storage)
 

Static Private Attributes

static QString s_lastError {QString()}
 
static bool s_valid {false}
 

Friends

class KisAllResourcesModel
 
class KisAllTagsModel
 
class KisDocument
 
class KisResourceLoaderRegistry
 
class KisResourceLocator
 
class KisResourceUserOperations
 
class TestResourceCacheDb
 
class TestResourceLocator
 

Detailed Description

The KisResourceCacheDb class encapsulates the database that caches information about the resources available to the user.

KisApplication creates and initializes the database. All other methods are static and can be used from anywhere.

Definition at line 23 of file KisResourceCacheDb.h.

Constructor & Destructor Documentation

◆ KisResourceCacheDb()

KisResourceCacheDb::KisResourceCacheDb ( )
explicitprivate

◆ ~KisResourceCacheDb()

KisResourceCacheDb::~KisResourceCacheDb ( )
private

Member Function Documentation

◆ addMetaDataForId()

bool KisResourceCacheDb::addMetaDataForId ( const QMap< QString, QVariant > map,
int id,
const QString & tableName )
staticprivate

Definition at line 2414 of file KisResourceCacheDb.cpp.

2415{
2416
2417 QSqlQuery q;
2418 if (!q.prepare("INSERT INTO metadata\n"
2419 "(foreign_id, table_name, key, value)\n"
2420 "VALUES\n"
2421 "(:id, :table, :key, :value)")) {
2422 QSqlDatabase::database().rollback();
2423 qWarning() << "Could not create insert metadata query" << q.lastError();
2424 return false;
2425 }
2426
2427 QMap<QString, QVariant>::const_iterator iter = map.cbegin();
2428 while (iter != map.cend()) {
2429 q.bindValue(":id", id);
2430 q.bindValue(":table", tableName);
2431 q.bindValue(":key", iter.key());
2432
2433 QVariant v = iter.value();
2434 if (!v.isNull() && v.isValid()) {
2435 QByteArray ba;
2436 QDataStream ds(&ba, QIODevice::WriteOnly);
2437 ds.setVersion(QDataStream::Qt_5_15); // so Qt6 can write metatypes readable by Qt5
2438 ds << v;
2439 ba = ba.toBase64();
2440 q.bindValue(":value", QString::fromLatin1(ba));
2441
2442 if (!q.exec()) {
2443 qWarning() << "Could not insert metadata" << q.lastError();
2444 return false;
2445 }
2446 }
2447 ++iter;
2448 }
2449 return true;
2450}
qreal v

References v.

◆ addResource()

bool KisResourceCacheDb::addResource ( KisResourceStorageSP storage,
QDateTime timestamp,
KoResourceSP resource,
const QString & resourceType )
staticprivate

Definition at line 1063 of file KisResourceCacheDb.cpp.

1064{
1065 bool r = false;
1066
1067 if (!s_valid) {
1068 qWarning() << "KisResourceCacheDb::addResource: The database is not valid";
1069 return false;
1070 }
1071
1072 if (!resource || !resource->valid()) {
1073 qWarning() << "KisResourceCacheDb::addResource: The resource is not valid:" << resource->filename();
1074 // We don't care about invalid resources and will just ignore them.
1075 return true;
1076 }
1077 bool temporary = (storage->type() == KisResourceStorage::StorageType::Memory);
1078
1079 // Check whether it already exists
1080 int resourceId = resourceIdForResource(resource->filename(), resourceType, KisResourceLocator::instance()->makeStorageLocationRelative(storage->location()));
1081 if (resourceId > -1) {
1082 return true;
1083 }
1084
1085 QSqlQuery q;
1086 r = q.prepare("INSERT INTO resources \n"
1087 "(storage_id, resource_type_id, name, filename, tooltip, thumbnail, status, temporary, md5sum) \n"
1088 "VALUES \n"
1089 "((SELECT id "
1090 " FROM storages "
1091 " WHERE location = :storage_location)\n"
1092 ", (SELECT id\n"
1093 " FROM resource_types\n"
1094 " WHERE name = :resource_type)\n"
1095 ", :name\n"
1096 ", :filename\n"
1097 ", :tooltip\n"
1098 ", :thumbnail\n"
1099 ", :status\n"
1100 ", :temporary\n"
1101 ", :md5sum)");
1102
1103 if (!r) {
1104 qWarning() << "Could not prepare addResource statement" << q.lastError();
1105 return r;
1106 }
1107
1108 q.bindValue(":resource_type", resourceType);
1109 q.bindValue(":storage_location", changeToEmptyIfNull(KisResourceLocator::instance()->makeStorageLocationRelative(storage->location())));
1110 q.bindValue(":name", resource->name());
1111 q.bindValue(":filename", resource->filename());
1112
1113 QString translationContext;
1114 if (storage->type() == KisResourceStorage::StorageType::Bundle) {
1115 translationContext = "./krita/data/bundles/" + KisResourceLocator::instance()->makeStorageLocationRelative(storage->location())
1116 + ":" + resourceType + "/" + resource->filename();
1117 } else if (storage->location() == "memory") {
1118 translationContext = "memory/" + resourceType + "/" + resource->filename();
1119 }
1120 else if (resource->filename().endsWith(".myb", Qt::CaseInsensitive)) {
1121 translationContext = "./plugins/paintops/mypaint/brushes/" + resource->filename();
1122 } else {
1123 translationContext = "./krita/data/" + resourceType + "/" + resource->filename();
1124 }
1125
1126 {
1127 QByteArray ctx = translationContext.toUtf8();
1128 QString translatedName = i18nc(ctx, resource->name().toUtf8());
1129 if (translatedName == resource->name()) {
1130 // Try using the file name without the file extension, and replaces '_' with spaces.
1131 QString altName = QFileInfo(resource->filename()).completeBaseName().replace('_', ' ');
1132 QString altTranslatedName = i18nc(ctx, altName.toUtf8());
1133 if (altName != altTranslatedName) {
1134 translatedName = altTranslatedName;
1135 }
1136 }
1137 q.bindValue(":tooltip", translatedName);
1138 }
1139
1140 QBuffer buf;
1141 buf.open(QBuffer::WriteOnly);
1142 resource->image().save(&buf, "PNG");
1143 buf.close();
1144 q.bindValue(":thumbnail", buf.data());
1145
1146 q.bindValue(":status", resource->active());
1147 q.bindValue(":temporary", (temporary ? 1 : 0));
1148 q.bindValue(":md5sum", resource->md5Sum());
1149
1150 r = q.exec();
1151 if (!r) {
1152 qWarning() << "Could not execute addResource statement" << q.lastError() << q.boundValues();
1153 return r;
1154 }
1155 resourceId = resourceIdForResource(resource->filename(), resourceType, KisResourceLocator::instance()->makeStorageLocationRelative(storage->location()));
1156
1157 if (resourceId < 0) {
1158
1159 qWarning() << "Adding to database failed, resource id after adding is " << resourceId << "! (Probable reason: the resource has the same filename, storage, resource type as an existing resource). Resource is: "
1160 << resource->name()
1161 << resource->filename()
1162 << resourceType
1164 return false;
1165 }
1166
1167 resource->setResourceId(resourceId);
1168
1169 if (!addResourceVersionImpl(resourceId, timestamp, storage, resource)) {
1170 qWarning() << "Could not add resource version" << resource;
1171 return false;
1172 }
1173
1174 if (!resource->metadata().isEmpty()) {
1175 return updateMetaDataForId(resource->metadata(), resource->resourceId(), METADATA_RESOURCES);
1176 }
1177
1178 return true;
1179
1180
1181}
QString changeToEmptyIfNull(QString s)
const QString METADATA_RESOURCES
static bool addResourceVersionImpl(int resourceId, QDateTime timestamp, KisResourceStorageSP storage, KoResourceSP resource)
static int resourceIdForResource(const QString &resourceFileName, const QString &resourceType, const QString &storageLocation)
static bool updateMetaDataForId(const QMap< QString, QVariant > map, int id, const QString &tableName)
setMetaDataForId removes all metadata for the given id and table name, and inserts the metadata in th...
QString makeStorageLocationRelative(QString location) const
static KisResourceLocator * instance()

References addResourceVersionImpl(), KisResourceStorage::Bundle, changeToEmptyIfNull(), KisResourceLocator::instance(), KisResourceLocator::makeStorageLocationRelative(), KisResourceStorage::Memory, METADATA_RESOURCES, resourceIdForResource(), s_valid, and updateMetaDataForId().

◆ addResources()

bool KisResourceCacheDb::addResources ( KisResourceStorageSP storage,
QString resourceType )
staticprivate

Definition at line 1183 of file KisResourceCacheDb.cpp.

1184{
1185 QSqlDatabase::database().transaction();
1186 QSharedPointer<KisResourceStorage::ResourceIterator> iter = storage->resources(resourceType);
1187 while (iter->hasNext()) {
1188 iter->next();
1189
1191 iter->versions();
1192
1193 int resourceId = -1;
1194
1195 while (verIt->hasNext()) {
1196 verIt->next();
1197
1198 KoResourceSP resource = verIt->resource();
1199 if (resource && resource->valid()) {
1200 resource->setVersion(verIt->guessedVersion());
1201 resource->setMD5Sum(storage->resourceMd5(verIt->url()));
1202
1203 if (resourceId < 0) {
1204 if (addResource(storage, iter->lastModified(), resource, iter->type())) {
1205 resourceId = resource->resourceId();
1206 } else {
1207 qWarning() << "Could not add resource" << resource->filename() << "to the database";
1208 }
1209 } else {
1210 if (!addResourceVersion(resourceId, iter->lastModified(), storage, resource)) {
1211 qWarning() << "Could not add resource version" << resource->filename() << "to the database";
1212 }
1213 }
1214 }
1215 }
1216 }
1217 QSqlDatabase::database().commit();
1218 return true;
1219}
static bool addResource(KisResourceStorageSP storage, QDateTime timestamp, KoResourceSP resource, const QString &resourceType)
static bool addResourceVersion(int resourceId, QDateTime timestamp, KisResourceStorageSP storage, KoResourceSP resource)
addResourceVersion adds a new version of the resource to the database. The resource itself already sh...

References addResource(), and addResourceVersion().

◆ addResourceVersion()

bool KisResourceCacheDb::addResourceVersion ( int resourceId,
QDateTime timestamp,
KisResourceStorageSP storage,
KoResourceSP resource )
staticprivate

addResourceVersion adds a new version of the resource to the database. The resource itself already should be updated with the updated filename and version.

Parameters
resourceIdunique identifier for the resource
timestamp
storage
resource
Returns
true if the database was successfully updated

Definition at line 646 of file KisResourceCacheDb.cpp.

647{
648 bool r = false;
649
650
651 r = addResourceVersionImpl(resourceId, timestamp, storage, resource);
652
653 if (!r) return r;
654
655 r = makeResourceTheCurrentVersion(resourceId, resource);
656
657 return r;
658}
static bool makeResourceTheCurrentVersion(int resourceId, KoResourceSP resource)

References addResourceVersionImpl(), and makeResourceTheCurrentVersion().

◆ addResourceVersionImpl()

bool KisResourceCacheDb::addResourceVersionImpl ( int resourceId,
QDateTime timestamp,
KisResourceStorageSP storage,
KoResourceSP resource )
staticprivate

Definition at line 660 of file KisResourceCacheDb.cpp.

661{
662 bool r = false;
663
664 // Create the new version. The resource is expected to have an updated version number, or
665 // this will fail on the unique index on resource_id, storage_id and version.
666 //
667 // This function **only** adds to the versioned_resources table.
668 // The resources table should be updated by the caller manually using
669 // updateResourceTableForResourceIfNeeded()
670
671 Q_ASSERT(resource->version() >= 0);
672
673 QSqlQuery q;
674 r = q.prepare("INSERT INTO versioned_resources \n"
675 "(resource_id, storage_id, version, filename, timestamp, md5sum)\n"
676 "VALUES\n"
677 "( :resource_id\n"
678 ", (SELECT id \n"
679 " FROM storages \n"
680 " WHERE location = :storage_location)\n"
681 ", :version\n"
682 ", :filename\n"
683 ", :timestamp\n"
684 ", :md5sum\n"
685 ");");
686
687 if (!r) {
688 qWarning() << "Could not prepare addResourceVersion statement" << q.lastError();
689 return r;
690 }
691
692 q.bindValue(":resource_id", resourceId);
693 q.bindValue(":storage_location", changeToEmptyIfNull(KisResourceLocator::instance()->makeStorageLocationRelative(storage->location())));
694 q.bindValue(":version", resource->version());
695 q.bindValue(":filename", resource->filename());
696 q.bindValue(":timestamp", timestamp.toSecsSinceEpoch());
697 KIS_SAFE_ASSERT_RECOVER_NOOP(!resource->md5Sum().isEmpty());
698 q.bindValue(":md5sum", resource->md5Sum());
699 r = q.exec();
700 if (!r) {
701
702 qWarning() << "Could not execute addResourceVersionImpl statement" << q.lastError() << resourceId << storage->name() << storage->location() << resource->name() << resource->filename() << "version" << resource->version();
703 return r;
704 }
705
706 return r;
707}
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130

References changeToEmptyIfNull(), KisResourceLocator::instance(), and KIS_SAFE_ASSERT_RECOVER_NOOP.

◆ addStorage()

bool KisResourceCacheDb::addStorage ( KisResourceStorageSP storage,
bool preinstalled )
staticprivate

Definition at line 1583 of file KisResourceCacheDb.cpp.

1584{
1585 bool r = true;
1586
1587 if (!s_valid) {
1588 qWarning() << "The database is not valid";
1589 return false;
1590 }
1591
1592 {
1593 QSqlQuery q;
1594 r = q.prepare("SELECT * FROM storages WHERE location = :location");
1595 q.bindValue(":location", changeToEmptyIfNull(KisResourceLocator::instance()->makeStorageLocationRelative(storage->location())));
1596 r = q.exec();
1597 if (!r) {
1598 qWarning() << "Could not select from storages";
1599 return r;
1600 }
1601 if (q.first()) {
1602 debugResource << "Storage already exists" << storage;
1603 return true;
1604 }
1605 }
1606
1607 // Insert the storage;
1608 {
1609 QSqlQuery q;
1610
1611 r = q.prepare("INSERT INTO storages\n "
1612 "(storage_type_id, location, timestamp, pre_installed, active, thumbnail)\n"
1613 "VALUES\n"
1614 "(:storage_type_id, :location, :timestamp, :pre_installed, :active, :thumbnail);");
1615
1616 if (!r) {
1617 qWarning() << "Could not prepare query" << q.lastError();
1618 return r;
1619 }
1620
1621 const QString sanitizedStorageLocation =
1622 changeToEmptyIfNull(KisResourceLocator::instance()->makeStorageLocationRelative(storage->location()));
1623
1624 q.bindValue(":storage_type_id", static_cast<int>(storage->type()));
1625 q.bindValue(":location", sanitizedStorageLocation);
1626 q.bindValue(":timestamp", storage->timestamp().toSecsSinceEpoch());
1627 q.bindValue(":pre_installed", preinstalled ? 1 : 0);
1628 q.bindValue(":active", !disabledBundles.contains(storage->name()));
1629
1630 QBuffer buf;
1631 buf.open(QBuffer::WriteOnly);
1632 storage->thumbnail().save(&buf, "PNG");
1633 buf.close();
1634 q.bindValue(":thumbnail", buf.data());
1635
1636 r = q.exec();
1637
1638 if (!r) qWarning() << "Could not execute query" << q.lastError();
1639
1640 if (!q.prepare("SELECT id\n"
1641 "FROM storages\n"
1642 "WHERE location = :location\n")) {
1643 qWarning() << "Could not prepare storage id statement" << q.lastError();
1644 }
1645
1646 q.bindValue(":location", sanitizedStorageLocation);
1647 if (!q.exec()) {
1648 qWarning() << "Could not execute storage id statement" << q.boundValues() << q.lastError();
1649 }
1650
1651 if (!q.first()) {
1652 qWarning() << "Could not find id for the newly added storage" << q.lastError();
1653 } else {
1654 storage->setStorageId(q.value("id").toInt());
1655 }
1656 }
1657
1658 // Insert the metadata
1659 {
1660 QStringList keys = storage->metaDataKeys();
1661 if (keys.size() > 0 && storage->storageId() >= 0) {
1662
1663 QMap<QString, QVariant> metadata;
1664
1665 Q_FOREACH(const QString &key, storage->metaDataKeys()) {
1666 metadata[key] = storage->metaData(key);
1667 }
1668
1669 updateMetaDataForId(metadata, storage->storageId(), METADATA_STORAGES);
1670 }
1671 }
1672
1673 Q_FOREACH(const QString &resourceType, KisResourceLoaderRegistry::instance()->resourceTypes()) {
1674 if (!KisResourceCacheDb::addResources(storage, resourceType)) {
1675 qWarning() << "Failed to add all resources for storage" << storage;
1676 r = false;
1677 }
1678 }
1679
1680 return r;
1681}
const QString METADATA_STORAGES
#define debugResource
static QStringList disabledBundles
the list of compatibility bundles that need to inactive by default
static bool addResources(KisResourceStorageSP storage, QString resourceType)
static KisResourceLoaderRegistry * instance()

References addResources(), changeToEmptyIfNull(), debugResource, disabledBundles, KisResourceLoaderRegistry::instance(), KisResourceLocator::instance(), METADATA_STORAGES, s_valid, and updateMetaDataForId().

◆ addStorageTags()

bool KisResourceCacheDb::addStorageTags ( KisResourceStorageSP storage)
staticprivate

Definition at line 1683 of file KisResourceCacheDb.cpp.

1684{
1685
1686 bool r = true;
1687 Q_FOREACH(const QString &resourceType, KisResourceLoaderRegistry::instance()->resourceTypes()) {
1688 if (!KisResourceCacheDb::addTags(storage, resourceType)) {
1689 qWarning() << "Failed to add all tags for storage" << storage;
1690 r = false;
1691 }
1692 }
1693 return r;
1694}
static bool addTags(KisResourceStorageSP storage, QString resourceType)

References addTags(), and KisResourceLoaderRegistry::instance().

◆ addTag()

bool KisResourceCacheDb::addTag ( const QString & resourceType,
const QString storageLocation,
KisTagSP tag )
staticprivate

Definition at line 1406 of file KisResourceCacheDb.cpp.

1407{
1408
1409 if (hasTag(tag->url(), resourceType)) {
1410 // Check whether this storage is already registered for this tag
1411 QSqlQuery q;
1412 if (!q.prepare("SELECT storages.location\n"
1413 "FROM tags_storages\n"
1414 ", tags\n"
1415 ", storages\n"
1416 "WHERE tags.id = tags_storages.tag_id\n"
1417 "AND storages.id = tags_storages.storage_id\n"
1418 "AND tags.resource_type_id = (SELECT id\n"
1419 " FROM resource_types\n"
1420 " WHERE name = :resource_type)\n"
1421 "AND tags.url = :url"))
1422 {
1423 qWarning() << "Could not prepare select tags from tags_storages query" << q.lastError();
1424 }
1425
1426 q.bindValue(":url", tag->url());
1427 q.bindValue(":resource_type", resourceType);
1428
1429 if (!q.exec()) {
1430 qWarning() << "Could not execute tags_storages query" << q.boundValues() << q.lastError();
1431 }
1432
1433 // If this tag is not yet linked to the storage, link it
1434 if (!q.first()) {
1435 return linkTagToStorage(tag->url(), resourceType, storageLocation);
1436 }
1437
1438 return true;
1439 }
1440
1441 int tagId;
1442
1443 // Insert the tag
1444 {
1445 QSqlQuery q;
1446 if (!q.prepare("INSERT INTO tags\n"
1447 "(url, name, comment, resource_type_id, active, filename)\n"
1448 "VALUES\n"
1449 "( :url\n"
1450 ", :name\n"
1451 ", :comment\n"
1452 ", (SELECT id\n"
1453 " FROM resource_types\n"
1454 " WHERE name = :resource_type)\n"
1455 ", 1\n"
1456 ", :filename\n"
1457 ");")) {
1458 qWarning() << "Could not prepare insert tag statement" << q.lastError();
1459 return false;
1460 }
1461
1462 q.bindValue(":url", tag->url());
1463 q.bindValue(":name", tag->name(false));
1464 q.bindValue(":comment", tag->comment(false));
1465 q.bindValue(":resource_type", resourceType);
1466 q.bindValue(":filename", tag->filename());
1467
1468 if (!q.exec()) {
1469 qWarning() << "Could not insert tag" << q.boundValues() << q.lastError();
1470 }
1471
1472 tagId = q.lastInsertId().toInt();
1473 }
1474
1475 {
1476 Q_FOREACH(const QString language, tag->names().keys()) {
1477
1478 QString name = tag->names()[language];
1479 QString comment = name;
1480 if (tag->comments().contains(language)) {
1481 comment = tag->comments()[language];
1482 }
1483
1484 QSqlQuery q;
1485 if (!q.prepare("INSERT INTO tag_translations\n"
1486 "( tag_id\n"
1487 ", language\n"
1488 ", name\n"
1489 ", comment\n"
1490 ")\n"
1491 "VALUES\n"
1492 "( :id\n"
1493 ", :language\n"
1494 ", :name\n"
1495 ", :comment\n"
1496 ");")) {
1497 qWarning() << "Could not prepare insert tag_translation query" << q.lastError();
1498 }
1499
1500 q.bindValue(":id", tagId);
1501 q.bindValue(":language", language);
1502 q.bindValue(":name", name);
1503 q.bindValue(":comment", comment);
1504
1505 if (!q.exec()) {
1506 qWarning() << "Could not execute insert tag_translation query" << q.lastError() << q.boundValues();
1507 }
1508 }
1509
1510 }
1511
1512
1513 linkTagToStorage(tag->url(), resourceType, storageLocation);
1514
1515 return true;
1516}
static bool linkTagToStorage(const QString &url, const QString &resourceType, const QString &storageLocation)
static bool hasTag(const QString &url, const QString &resourceType)
const char * name(StandardAction id)

References hasTag(), and linkTagToStorage().

◆ addTags()

bool KisResourceCacheDb::addTags ( KisResourceStorageSP storage,
QString resourceType )
staticprivate

Definition at line 1518 of file KisResourceCacheDb.cpp.

1519{
1520 QSqlDatabase::database().transaction();
1521 QSharedPointer<KisResourceStorage::TagIterator> iter = storage->tags(resourceType);
1522 while(iter->hasNext()) {
1523 iter->next();
1524 KisTagSP tag = iter->tag();
1525 if (tag && tag->valid()) {
1526 if (!addTag(resourceType, storage->location(), tag)) {
1527 qWarning() << "Could not add tag" << tag << "to the database";
1528 continue;
1529 }
1530 if (!tag->defaultResources().isEmpty()) {
1531 Q_FOREACH(const QString &resourceFileName, tag->defaultResources()) {
1532 if (!tagResource(resourceFileName, tag, resourceType)) {
1533 qWarning() << "Could not tag resource" << QFileInfo(resourceFileName).baseName() << "from" << storage->name() << "filename" << resourceFileName << "with tag" << iter->tag();
1534 }
1535 }
1536 }
1537 }
1538 }
1539 QSqlDatabase::database().commit();
1540 return true;
1541}
static bool addTag(const QString &resourceType, const QString storageLocation, KisTagSP tag)
static bool tagResource(const QString &resourceFileName, KisTagSP tag, const QString &resourceType)

References addTag(), and tagResource().

◆ deleteStorage() [1/2]

bool KisResourceCacheDb::deleteStorage ( KisResourceStorageSP storage)
staticprivate

Actually delete the storage and all its resources from the database (i.e., nothing is set to inactive, it's deleted)

Definition at line 1808 of file KisResourceCacheDb.cpp.

1809{
1810 return deleteStorage(KisResourceLocator::instance()->makeStorageLocationRelative(storage->location()));
1811}
static bool deleteStorage(KisResourceStorageSP storage)
Actually delete the storage and all its resources from the database (i.e., nothing is set to inactive...

References deleteStorage(), and KisResourceLocator::instance().

◆ deleteStorage() [2/2]

bool KisResourceCacheDb::deleteStorage ( QString location)
staticprivate

Actually delete the storage and all its resources from the database (i.e., nothing is set to inactive, it's deleted) location - relative

Definition at line 1696 of file KisResourceCacheDb.cpp.

1697{
1698 // location is already relative
1699
1700 try {
1701 KisDatabaseTransactionLock transactionLock(QSqlDatabase::database());
1702
1703 {
1704 KisSqlQueryLoader loader(":/sql/delete_versioned_resources_for_storage_indirect.sql",
1706 loader.query().bindValue(":location", changeToEmptyIfNull(location));
1707 loader.exec();
1708 }
1709
1710 {
1711 KisSqlQueryLoader loader(":/sql/delete_versioned_resources_for_storage_direct.sql",
1713 loader.query().bindValue(":location", changeToEmptyIfNull(location));
1714 loader.exec();
1715 if (loader.query().numRowsAffected() > 0) {
1716 qWarning() << "WARNING: deleteStorage: versioned_resurces table contained resource versions not being "
1717 "present in the main table. Deleted: "
1718 << loader.query().numRowsAffected();
1719 }
1720 }
1721
1722 {
1723 KisSqlQueryLoader loader(":/sql/delete_resource_metadata_for_storage.sql",
1725 loader.query().bindValue(":location", changeToEmptyIfNull(location));
1726 loader.query().bindValue(":table", METADATA_RESOURCES);
1727 loader.exec();
1728 }
1729
1730 {
1731 KisSqlQueryLoader loader("inline://delete_current_resources_for_storage",
1732 "DELETE FROM resources\n"
1733 "WHERE storage_id = (SELECT storages.id\n"
1734 " FROM storages\n"
1735 " WHERE storages.location = :location)\n",
1737 loader.query().bindValue(":location", changeToEmptyIfNull(location));
1738 loader.exec();
1739 }
1740
1741 {
1742 KisSqlQueryLoader loader("inline://delete_tags_for_storage",
1743 "DELETE FROM tags \n"
1744 "WHERE id IN (SELECT tags_storages.tag_id \n "
1745 " FROM tags_storages \n"
1746 " WHERE tags_storages.storage_id = \n"
1747 " (SELECT storages.id\n"
1748 " FROM storages\n"
1749 " WHERE storages.location = :location)\n"
1750 " )",
1752 loader.query().bindValue(":location", changeToEmptyIfNull(location));
1753 loader.exec();
1754 }
1755
1756 {
1757 KisSqlQueryLoader loader("inline://delete_tags_storage_links_for_storage",
1758 "DELETE FROM tags_storages \n"
1759 "WHERE tags_storages.storage_id = \n"
1760 " (SELECT storages.id\n"
1761 " FROM storages\n"
1762 " WHERE storages.location = :location)",
1764 loader.query().bindValue(":location", changeToEmptyIfNull(location));
1765 loader.exec();
1766 }
1767
1768 {
1769 KisSqlQueryLoader loader("inline://delete_starage_metadata_for_storage",
1770 "DELETE FROM metadata\n"
1771 "WHERE foreign_id = (SELECT storages.id\n"
1772 " FROM storages\n"
1773 " WHERE storages.location = :location)"
1774 "AND table_name = :table;",
1776 loader.query().bindValue(":location", changeToEmptyIfNull(location));
1777 loader.query().bindValue(":table", METADATA_STORAGES);
1778 loader.exec();
1779 }
1780
1781 {
1782 KisSqlQueryLoader loader("inline://delete_storage",
1783 "DELETE FROM storages\n"
1784 "WHERE location = :location;",
1786 loader.query().bindValue(":location", changeToEmptyIfNull(location));
1787 loader.exec();
1788 }
1789
1790 transactionLock.commit();
1791
1792 } catch (const KisSqlQueryLoader::FileException &e) {
1793 qWarning().noquote() << "ERROR: deleteStorage:" << e.message;
1794 qWarning().noquote() << " file:" << e.filePath;
1795 qWarning().noquote() << " error:" << e.fileErrorString;
1796 return false;
1797 } catch (const KisSqlQueryLoader::SQLException &e) {
1798 qWarning().noquote() << "ERROR: deleteStorage:" << e.message;
1799 qWarning().noquote() << " file:" << e.filePath;
1800 qWarning().noquote() << " statement:" << e.statementIndex;
1801 qWarning().noquote() << " error:" << e.sqlError.text();
1802 return false;
1803 }
1804
1805 return true;
1806}
static constexpr single_statement_mode_t single_statement_mode

References changeToEmptyIfNull(), detail::KisDatabaseTransactionLockAdapter::commit(), KisSqlQueryLoader::exec(), KisSqlQueryLoader::FileException::fileErrorString, KisSqlQueryLoader::FileException::filePath, KisSqlQueryLoader::FileException::message, METADATA_RESOURCES, METADATA_STORAGES, KisSqlQueryLoader::query(), and KisSqlQueryLoader::single_statement_mode.

◆ deleteTemporaryResources()

void KisResourceCacheDb::deleteTemporaryResources ( )
static

Delete all storages that are Unknown or Memory and all resources that are marked temporary or belong to Unknown or Memory storages.

Remove all temporary resources

Remove all temporary storages

Definition at line 2123 of file KisResourceCacheDb.cpp.

2124{
2125 try {
2126 KisDatabaseTransactionLock transactionLock(QSqlDatabase::database());
2127
2131 {
2132 KisSqlQueryLoader loader(
2133 "inline://delete_metadata_for_resources_in_memory_storages",
2134 "DELETE FROM metadata\n"
2135 "WHERE foreign_id IN (SELECT id\n"
2136 " FROM resources\n"
2137 " WHERE storage_id in (SELECT id\n"
2138 " FROM storages\n"
2139 " WHERE storage_type_id == :storage_type))\n"
2140 "AND table_name = :table",
2142 loader.query().bindValue(":table", METADATA_RESOURCES);
2143 loader.query().bindValue(":storage_type", (int)KisResourceStorage::StorageType::Memory);
2144 loader.exec();
2145 }
2146
2147 {
2148 KisSqlQueryLoader loader("inline://delete_metadata_for_temporary_resources",
2149 "DELETE FROM metadata\n"
2150 "WHERE foreign_id IN (SELECT id\n"
2151 " FROM resources\n"
2152 " WHERE temporary = 1)\n"
2153 "AND table_name = :table",
2155 loader.query().bindValue(":table", METADATA_RESOURCES);
2156 loader.exec();
2157 }
2158
2159 {
2160 KisSqlQueryLoader loader("inline://delete_versions_of_resources_in_temporary_storages",
2161 "DELETE FROM versioned_resources\n"
2162 "WHERE storage_id in (SELECT id\n"
2163 " FROM storages\n"
2164 " WHERE storage_type_id == :storage_type)",
2166 loader.query().bindValue(":storage_type", (int)KisResourceStorage::StorageType::Memory);
2167 loader.exec();
2168 }
2169
2170 {
2171 KisSqlQueryLoader loader("inline://delete_versions_of_temporary_resources",
2172 "DELETE FROM versioned_resources\n"
2173 "WHERE resource_id IN (SELECT id FROM resources\n"
2174 " WHERE temporary = 1)",
2176 loader.exec();
2177 }
2178
2179 {
2180 KisSqlQueryLoader loader("inline://delete_current_resources_in_temporary_storages",
2181 "DELETE FROM resources\n"
2182 "WHERE storage_id in (SELECT id\n"
2183 " FROM storages\n"
2184 " WHERE storage_type_id == :storage_type)",
2186 loader.query().bindValue(":storage_type", (int)KisResourceStorage::StorageType::Memory);
2187 loader.exec();
2188 }
2189
2190 {
2191 KisSqlQueryLoader loader("inline://delete_current_temporary_resources",
2192 "DELETE FROM resources\n"
2193 "WHERE temporary = 1",
2195 loader.exec();
2196 }
2197
2202 {
2203 KisSqlQueryLoader loader("inline://delete_metadata_for_temporary_storages",
2204 "DELETE FROM metadata\n"
2205 "WHERE foreign_id IN (SELECT id\n"
2206 " FROM storages\n"
2207 " WHERE storage_type_id == :storage_type)\n"
2208 "AND table_name = :table;",
2210 loader.query().bindValue(":storage_type", (int)KisResourceStorage::StorageType::Memory);
2211 loader.query().bindValue(":table", METADATA_STORAGES);
2212 loader.exec();
2213 }
2214
2215 {
2216 KisSqlQueryLoader loader("inline://delete_temporary_storages",
2217 "DELETE FROM storages\n"
2218 "WHERE storage_type_id == :storage_type\n",
2220 loader.query().bindValue(":storage_type", (int)KisResourceStorage::StorageType::Memory);
2221 loader.exec();
2222 }
2223
2224 transactionLock.commit();
2225 } catch (const KisSqlQueryLoader::SQLException &e) {
2226 qWarning().noquote() << "ERROR: failed to execute query:" << e.message;
2227 qWarning().noquote() << " file:" << e.filePath;
2228 qWarning().noquote() << " statement:" << e.statementIndex;
2229 qWarning().noquote() << " error:" << e.sqlError.text();
2230 }
2231}

References detail::KisDatabaseTransactionLockAdapter::commit(), KisSqlQueryLoader::exec(), KisSqlQueryLoader::SQLException::filePath, KisResourceStorage::Memory, KisSqlQueryLoader::SQLException::message, METADATA_RESOURCES, METADATA_STORAGES, KisSqlQueryLoader::query(), KisSqlQueryLoader::single_statement_mode, KisSqlQueryLoader::SQLException::sqlError, and KisSqlQueryLoader::SQLException::statementIndex.

◆ getAllVersionsLocations()

bool KisResourceCacheDb::getAllVersionsLocations ( int resourceId,
QStringList & outVersionsLocationsList )
staticprivate

Definition at line 1036 of file KisResourceCacheDb.cpp.

1037{
1038 QSqlQuery q;
1039 bool r = q.prepare("SELECT filename FROM versioned_resources \n"
1040 "WHERE resource_id = :resource_id;");
1041
1042 if (!r) {
1043 qWarning() << "Could not prepare getAllVersionsLocations statement" << q.lastError();
1044 return r;
1045 }
1046
1047 q.bindValue(":resource_id", resourceId);
1048 r = q.exec();
1049 if (!r) {
1050 qWarning() << "Could not execute getAllVersionsLocations statement" << q.lastError() << resourceId;
1051 return r;
1052 }
1053
1054 outVersionsLocationsList = QStringList();
1055 while (q.next()) {
1056 outVersionsLocationsList << q.value("filename").toString();
1057 }
1058
1059 return r;
1060
1061}
QList< QString > QStringList

◆ getForeignKeysStateImpl()

bool KisResourceCacheDb::getForeignKeysStateImpl ( )
static

get the foreign_keys feature state of the database (the function may throw SQL exceptions, call in a try-block only!)

Definition at line 2253 of file KisResourceCacheDb.cpp.

2254{
2255 KisSqlQueryLoader loader("inline://get_foreign_keys_state",
2256 "PRAGMA foreign_keys");
2257
2258 loader.exec();
2259
2260 if (loader.query().first()) {
2261 return loader.query().value(0).toInt();
2262 }
2263
2264 return false;
2265}

References KisSqlQueryLoader::exec(), and KisSqlQueryLoader::query().

◆ getResourceIdFromFilename()

bool KisResourceCacheDb::getResourceIdFromFilename ( QString filename,
QString resourceType,
QString storageLocation,
int & outResourceId )
staticprivate

The function will find the resource only if it is the latest version.

Definition at line 961 of file KisResourceCacheDb.cpp.

962{
963 QSqlQuery q;
964
965 bool r = q.prepare("SELECT resources.id FROM resources\n"
966 ", resource_types\n"
967 ", storages\n"
968 "WHERE resources.filename = :filename\n" // bind to filename
969 "AND resource_types.id = resources.resource_type_id\n" // join resources_types + resources
970 "AND resource_types.name = :resourceType\n" // bind to resource type
971 "AND resources.storage_id = storages.id\n" // join resources + storages
972 "AND storages.location = :storageLocation"); // bind to storage location
973
974 if (!r) {
975 qWarning() << "Could not prepare getResourceIdFromFilename statement" << q.lastError() << q.executedQuery();
976 return r;
977 }
978
979 q.bindValue(":filename", filename);
980 q.bindValue(":resourceType", resourceType);
981 q.bindValue(":storageLocation", changeToEmptyIfNull(storageLocation));
982
983 r = q.exec();
984 if (!r) {
985 qWarning() << "Could not execute getResourceIdFromFilename statement" << q.lastError() << filename << resourceType;
986 return r;
987 }
988
989 r = q.first();
990 if (r) {
991 outResourceId = q.value("resources.id").toInt();
992 }
993
994 return r;
995}

References changeToEmptyIfNull().

◆ getResourceIdFromVersionedFilename()

bool KisResourceCacheDb::getResourceIdFromVersionedFilename ( QString filename,
QString resourceType,
QString storageLocation,
int & outResourceId )
staticprivate

Note that here you can put even the original filename - any filename from the versioned_resources - and it will still find it.

Definition at line 997 of file KisResourceCacheDb.cpp.

998{
999 QSqlQuery q;
1000
1001 bool r = q.prepare("SELECT resource_id FROM versioned_resources\n"
1002 ", resources\n"
1003 ", resource_types\n"
1004 ", storages\n"
1005 "WHERE versioned_resources.filename = :filename\n" // bind to filename
1006 "AND resources.id = versioned_resources.resource_id\n" // join resources + versioned_resources
1007 "AND resource_types.id = resources.resource_type_id\n" // join resources_types + resources
1008 "AND resource_types.name = :resourceType\n" // bind to resource type
1009 "AND resources.storage_id = storages.id\n" // join resources + storages
1010 "AND storages.location = :storageLocation"); // bind to storage location
1011
1012 if (!r) {
1013 qWarning() << "Could not prepare getResourceIdFromVersionedFilename statement" << q.lastError() << q.executedQuery();
1014 return r;
1015 }
1016
1017
1018 q.bindValue(":filename", filename);
1019 q.bindValue(":resourceType", resourceType);
1020 q.bindValue(":storageLocation", changeToEmptyIfNull(storageLocation));
1021
1022 r = q.exec();
1023 if (!r) {
1024 qWarning() << "Could not execute getResourceIdFromVersionedFilename statement" << q.lastError() << filename << resourceType;
1025 return r;
1026 }
1027
1028 r = q.first();
1029 if (r) {
1030 outResourceId = q.value("resource_id").toInt();
1031 }
1032
1033 return r;
1034}

References changeToEmptyIfNull().

◆ hasTag()

bool KisResourceCacheDb::hasTag ( const QString & url,
const QString & resourceType )
staticprivate

Definition at line 1351 of file KisResourceCacheDb.cpp.

1352{
1353 QFile f(":/select_tag.sql");
1354 if (f.open(QFile::ReadOnly)) {
1355 QSqlQuery q;
1356 if (!q.prepare(f.readAll())) {
1357 qWarning() << "Could not read and prepare select_tag.sql" << q.lastError();
1358 return false;
1359 }
1360 q.bindValue(":url", url);
1361 q.bindValue(":resource_type", resourceType);
1362 if (!q.exec()) {
1363 qWarning() << "Could not query tags" << q.boundValues() << q.lastError();
1364 }
1365 return q.first();
1366 }
1367 qWarning() << "Could not open select_tag.sql";
1368 return false;
1369}

◆ initialize()

bool KisResourceCacheDb::initialize ( const QString & location)
static

initializes the database and updates the scheme if necessary. Does not actually fill the database with pointers to resources.

Parameters
locationthe location of the database
Returns
true if the database has been initialized correctly

Definition at line 480 of file KisResourceCacheDb.cpp.

481{
482 QSqlError err = createDatabase(location);
483
484 s_valid = !err.isValid();
485 switch (err.type()) {
486 case QSqlError::NoError:
487 s_lastError = QString();
488 break;
489 case QSqlError::ConnectionError:
490 s_lastError = QString("Could not initialize the resource cache database. Connection error: %1").arg(err.text());
491 break;
492 case QSqlError::StatementError:
493 s_lastError = QString("Could not initialize the resource cache database. Statement error: %1").arg(err.text());
494 break;
495 case QSqlError::TransactionError:
496 s_lastError = QString("Could not initialize the resource cache database. Transaction error: %1").arg(err.text());
497 break;
498 case QSqlError::UnknownError:
499 s_lastError = QString("Could not initialize the resource cache database. Unknown error: %1").arg(err.text());
500 break;
501 }
502
503 // Delete all storages that are no longer known to the resource locator (including the memory storages)
505
506 return s_valid;
507}
QSqlError createDatabase(const QString &location)
static void deleteTemporaryResources()
Delete all storages that are Unknown or Memory and all resources that are marked temporary or belong ...
static QString s_lastError

References createDatabase(), deleteTemporaryResources(), s_lastError, and s_valid.

◆ isValid()

bool KisResourceCacheDb::isValid ( )
static

isValid

Returns
true if the database has been correctly created, false if the database cannot be used

Definition at line 53 of file KisResourceCacheDb.cpp.

54{
55 return s_valid;
56}

References s_valid.

◆ lastError()

QString KisResourceCacheDb::lastError ( )
static

lastError returns the last SQL error.

Definition at line 58 of file KisResourceCacheDb.cpp.

59{
60 return s_lastError;
61}

References s_lastError.

◆ linkTagToStorage()

bool KisResourceCacheDb::linkTagToStorage ( const QString & url,
const QString & resourceType,
const QString & storageLocation )
staticprivate

Definition at line 1371 of file KisResourceCacheDb.cpp.

1372{
1373 QSqlQuery q;
1374 if (!q.prepare("INSERT INTO tags_storages\n"
1375 "(tag_id, storage_id)\n"
1376 "VALUES\n"
1377 "(\n"
1378 " ( SELECT id\n"
1379 " FROM tags\n"
1380 " WHERE url = :url\n"
1381 " AND resource_type_id = (SELECT id \n"
1382 " FROM resource_types\n"
1383 " WHERE name = :resource_type)"
1384 " )\n"
1385 ",( SELECT id\n"
1386 " FROM storages\n"
1387 " WHERE location = :storage_location\n"
1388 " )\n"
1389 ");")) {
1390 qWarning() << "Could not prepare add tag/storage statement" << q.lastError();
1391 return false;
1392 }
1393
1394 q.bindValue(":url", url);
1395 q.bindValue(":resource_type", resourceType);
1396 q.bindValue(":storage_location", changeToEmptyIfNull(KisResourceLocator::instance()->makeStorageLocationRelative(storageLocation)));
1397
1398 if (!q.exec()) {
1399 qWarning() << "Could not insert tag/storage link" << q.boundValues() << q.lastError();
1400 return false;
1401 }
1402 return true;
1403}

References changeToEmptyIfNull(), and KisResourceLocator::instance().

◆ makeResourceTheCurrentVersion()

bool KisResourceCacheDb::makeResourceTheCurrentVersion ( int resourceId,
KoResourceSP resource )
staticprivate

Definition at line 838 of file KisResourceCacheDb.cpp.

839{
840 bool r = false;
841
842 QSqlQuery q;
843 r = q.prepare("UPDATE resources\n"
844 "SET name = :name\n"
845 ", filename = :filename\n"
846 ", tooltip = :tooltip\n"
847 ", thumbnail = :thumbnail\n"
848 ", status = 1\n"
849 ", md5sum = :md5sum\n"
850 "WHERE id = :id");
851 if (!r) {
852 qWarning() << "Could not prepare updateResource statement" << q.lastError();
853 return r;
854 }
855
856 q.bindValue(":name", resource->name());
857 q.bindValue(":filename", resource->filename());
858 q.bindValue(":tooltip", i18n(resource->name().toUtf8()));
859 q.bindValue(":md5sum", resource->md5Sum());
860
861 QBuffer buf;
862 buf.open(QBuffer::WriteOnly);
863 resource->thumbnail().save(&buf, "PNG");
864 buf.close();
865 q.bindValue(":thumbnail", buf.data());
866 q.bindValue(":id", resourceId);
867
868 r = q.exec();
869 if (!r) {
870 qWarning() << "Could not update resource" << q.boundValues() << q.lastError();
871 }
872
873 if (!resource->metadata().isEmpty()) {
874 return updateMetaDataForId(resource->metadata(), resourceId, METADATA_RESOURCES);
875 }
876
877 return r;
878}

References METADATA_RESOURCES, and updateMetaDataForId().

◆ metaDataForId()

QMap< QString, QVariant > KisResourceCacheDb::metaDataForId ( int id,
const QString & tableName )
staticprivate

metaDataForId

Parameters
id
tableName
Returns

Definition at line 2342 of file KisResourceCacheDb.cpp.

2343{
2344 QMap<QString, QVariant> map;
2345
2346 QSqlQuery q;
2347 q.setForwardOnly(true);
2348 if (!q.prepare("SELECT key\n"
2349 ", value\n"
2350 "FROM metadata\n"
2351 "WHERE foreign_id = :id\n"
2352 "AND table_name = :table")) {
2353 qWarning() << "Could not prepare metadata query" << q.lastError();
2354 return map;
2355 }
2356
2357 q.bindValue(":id", id);
2358 q.bindValue(":table", tableName);
2359
2360 if (!q.exec()) {
2361 qWarning() << "Could not execute metadata query" << q.lastError();
2362 return map;
2363 }
2364
2365 while (q.next()) {
2366 QString key = q.value(0).toString();
2367 QByteArray ba = q.value(1).toByteArray();
2368 if (!ba.isEmpty()) {
2369 QDataStream ds(QByteArray::fromBase64(ba));
2370 QVariant value;
2371 ds.setVersion(QDataStream::Qt_5_15); // so Qt6 can read metatypes written by Qt5
2372 ds >> value;
2373 map[key] = value;
2374 }
2375 }
2376
2377 return map;
2378}
float value(const T *src, size_t ch)

References value().

◆ operator=()

KisResourceCacheDb KisResourceCacheDb::operator= ( const KisResourceCacheDb & )
private

◆ performHouseKeepingOnExit()

void KisResourceCacheDb::performHouseKeepingOnExit ( )
static

perform optimize and vacuum when necessary

Definition at line 2233 of file KisResourceCacheDb.cpp.

2234{
2235 QSqlQuery q;
2236
2237 if (!q.prepare("PRAGMA optimize;")) {
2238 qWarning() << "Could not prepare query" << q.lastQuery() << q.lastError();
2239 }
2240
2241 if (!q.exec()) {
2242 qWarning() << "Could not execute query" << q.lastQuery() << q.lastError();
2243 }
2244}

◆ registerResourceType()

bool KisResourceCacheDb::registerResourceType ( const QString & resourceType)
staticprivate

registerResourceType registers this resource type in the database

Parameters
resourceTypethe string that represents the type
Returns
true if the type was registered or had already been registered

Definition at line 2304 of file KisResourceCacheDb.cpp.

2305{
2306 // Check whether the type already exists
2307 {
2308 QSqlQuery q;
2309 if (!q.prepare("SELECT count(*)\n"
2310 "FROM resource_types\n"
2311 "WHERE name = :resource_type\n")) {
2312 qWarning() << "Could not prepare select from resource_types query" << q.lastError();
2313 return false;
2314 }
2315 q.bindValue(":resource_type", resourceType);
2316 if (!q.exec()) {
2317 qWarning() << "Could not execute select from resource_types query" << q.lastError();
2318 return false;
2319 }
2320 q.first();
2321 int rowCount = q.value(0).toInt();
2322 if (rowCount > 0) {
2323 return true;
2324 }
2325 }
2326 // if not, add it
2327 QFile f(":/fill_resource_types.sql");
2328 if (f.open(QFile::ReadOnly)) {
2329 QString sql = f.readAll();
2330 QSqlQuery q(sql);
2331 q.addBindValue(resourceType);
2332 if (!q.exec()) {
2333 qWarning() << "Could not insert" << resourceType << q.lastError();
2334 return false;
2335 }
2336 return true;
2337 }
2338 qWarning() << "Could not open fill_resource_types.sql";
2339 return false;
2340}

◆ registerStorageType()

bool KisResourceCacheDb::registerStorageType ( const KisResourceStorage::StorageType storageType)
staticprivate

registerStorageType registers this storage type in the database

Parameters
storageTypethe enum value that represents the type
Returns
true if the type was registered or had already been registered

Definition at line 1543 of file KisResourceCacheDb.cpp.

1544{
1545 // Check whether the type already exists
1546 const QString name = KisResourceStorage::storageTypeToUntranslatedString(storageType);
1547
1548 {
1549 QSqlQuery q;
1550 if (!q.prepare("SELECT count(*)\n"
1551 "FROM storage_types\n"
1552 "WHERE name = :storage_type\n")) {
1553 qWarning() << "Could not prepare select from storage_types query" << q.lastError();
1554 return false;
1555 }
1556 q.bindValue(":storage_type", name);
1557 if (!q.exec()) {
1558 qWarning() << "Could not execute select from storage_types query" << q.lastError();
1559 return false;
1560 }
1561 q.first();
1562 int rowCount = q.value(0).toInt();
1563 if (rowCount > 0) {
1564 return true;
1565 }
1566 }
1567 // if not, add it
1568 QFile f(":/fill_storage_types.sql");
1569 if (f.open(QFile::ReadOnly)) {
1570 QString sql = f.readAll();
1571 QSqlQuery q(sql);
1572 q.addBindValue(name);
1573 if (!q.exec()) {
1574 qWarning() << "Could not insert" << name << q.lastError();
1575 return false;
1576 }
1577 return true;
1578 }
1579 qWarning() << "Could not open fill_storage_types.sql";
1580 return false;
1581}
static QString storageTypeToUntranslatedString(StorageType storageType)

References KisResourceStorage::storageTypeToUntranslatedString().

◆ removeOrphanedMetaData()

bool KisResourceCacheDb::removeOrphanedMetaData ( )
staticprivate

removeOrphanedMetaData Previous versions of Krita never removed metadata, so this function doublechecks and removes any orphaned metadata for either storages or resources from the database.

Returns
true if successful, false if not

Definition at line 2452 of file KisResourceCacheDb.cpp.

2453{
2454 auto deleteMetadataForType = [] (const QString &tableName) {
2455 KisSqlQueryLoader loader("inline://delete_orphaned_records (" + tableName + ")",
2456 QString("DELETE FROM metadata\n"
2457 "WHERE foreign_id NOT IN (SELECT id FROM %1)\n"
2458 "AND table_name = \"%1\"\n")
2459 .arg(tableName));
2460 loader.exec();
2461
2462 if (loader.query().numRowsAffected() > 0) {
2463 qWarning().noquote().nospace() << "WARNING: orphaned metadata records were found for " << tableName << "!";
2464 qWarning().noquote().nospace() << " Num records removed: " << loader.query().numRowsAffected();
2465 }
2466 };
2467
2468 try {
2469 KisDatabaseTransactionLock transactionLock(QSqlDatabase::database());
2470
2471 deleteMetadataForType(METADATA_RESOURCES);
2472 deleteMetadataForType(METADATA_STORAGES);
2473
2474 transactionLock.commit();
2475
2476 } catch (const KisSqlQueryLoader::SQLException &e) {
2477 qWarning().noquote() << "ERROR: failed to execute query:" << e.message;
2478 qWarning().noquote() << " file:" << e.filePath;
2479 qWarning().noquote() << " statement:" << e.statementIndex;
2480 qWarning().noquote() << " error:" << e.sqlError.text();
2481
2482 return false;
2483 }
2484
2485 return true;
2486}

References detail::KisDatabaseTransactionLockAdapter::commit(), KisSqlQueryLoader::exec(), KisSqlQueryLoader::SQLException::filePath, KisSqlQueryLoader::SQLException::message, METADATA_RESOURCES, METADATA_STORAGES, KisSqlQueryLoader::query(), KisSqlQueryLoader::SQLException::sqlError, and KisSqlQueryLoader::SQLException::statementIndex.

◆ removeResourceCompletely()

bool KisResourceCacheDb::removeResourceCompletely ( int resourceId)
staticprivate

Definition at line 880 of file KisResourceCacheDb.cpp.

881{
882 bool r = false;
883
884 {
885 QSqlQuery q;
886 r = q.prepare("DELETE FROM versioned_resources \n"
887 "WHERE resource_id = :resource_id;");
888
889 if (!r) {
890 qWarning() << "Could not prepare removeResourceCompletely1 statement" << q.lastError();
891 return r;
892 }
893
894 q.bindValue(":resource_id", resourceId);
895 r = q.exec();
896 if (!r) {
897 qWarning() << "Could not execute removeResourceCompletely1 statement" << q.lastError() << resourceId;
898 return r;
899 }
900 }
901
902 {
903 QSqlQuery q;
904 r = q.prepare("DELETE FROM resources \n"
905 "WHERE id = :resource_id;");
906
907 if (!r) {
908 qWarning() << "Could not prepare removeResourceCompletely2 statement" << q.lastError();
909 return r;
910 }
911
912 q.bindValue(":resource_id", resourceId);
913 r = q.exec();
914 if (!r) {
915 qWarning() << "Could not execute removeResourceCompletely2 statement" << q.lastError() << resourceId;
916 return r;
917 }
918 }
919
920 {
921 QSqlQuery q;
922 r = q.prepare("DELETE FROM resource_tags \n"
923 "WHERE resource_id = :resource_id;");
924
925 if (!r) {
926 qWarning() << "Could not prepare removeResourceCompletely3 statement" << q.lastError();
927 return r;
928 }
929
930 q.bindValue(":resource_id", resourceId);
931 r = q.exec();
932 if (!r) {
933 qWarning() << "Could not execute removeResourceCompletely3 statement" << q.lastError() << resourceId;
934 return r;
935 }
936 }
937
938 {
939 QSqlQuery q;
940 r = q.prepare("DELETE FROM metadata \n"
941 "WHERE foreign_id = :resource_id\n"
942 "AND table_name = :table;");
943
944 if (!r) {
945 qWarning() << "Could not prepare removeResourceCompletely4 statement" << q.lastError();
946 return r;
947 }
948
949 q.bindValue(":resource_id", resourceId);
950 q.bindValue(":table", METADATA_RESOURCES);
951 r = q.exec();
952 if (!r) {
953 qWarning() << "Could not execute removeResourceCompletely4 statement" << q.lastError() << resourceId;
954 return r;
955 }
956 }
957
958 return r;
959}

References METADATA_RESOURCES.

◆ removeResourceVersionImpl()

bool KisResourceCacheDb::removeResourceVersionImpl ( int resourceId,
int version,
KisResourceStorageSP storage )
staticprivate

Definition at line 709 of file KisResourceCacheDb.cpp.

710{
711 bool r = false;
712
713 // Remove a version of the resource. This function **only** removes data from
714 // the versioned_resources table. The resources table should be updated by
715 // the caller manually using updateResourceTableForResourceIfNeeded()
716
717 QSqlQuery q;
718 r = q.prepare("DELETE FROM versioned_resources \n"
719 "WHERE resource_id = :resource_id\n"
720 "AND version = :version\n"
721 "AND storage_id = (SELECT id \n"
722 " FROM storages \n"
723 " WHERE location = :storage_location);");
724
725 if (!r) {
726 qWarning() << "Could not prepare removeResourceVersionImpl statement" << q.lastError();
727 return r;
728 }
729
730 q.bindValue(":resource_id", resourceId);
731 q.bindValue(":storage_location", changeToEmptyIfNull(KisResourceLocator::instance()->makeStorageLocationRelative(storage->location())));
732 q.bindValue(":version", version);
733 r = q.exec();
734 if (!r) {
735
736 qWarning() << "Could not execute removeResourceVersionImpl statement" << q.lastError() << resourceId << storage->name() << storage->location() << "version" << version;
737 return r;
738 }
739
740 return r;
741}

References changeToEmptyIfNull(), and KisResourceLocator::instance().

◆ resourceIdForResource()

int KisResourceCacheDb::resourceIdForResource ( const QString & resourceFileName,
const QString & resourceType,
const QString & storageLocation )
staticprivate

Definition at line 543 of file KisResourceCacheDb.cpp.

544{
545 //qDebug() << "resourceIdForResource" << resourceName << resourceFileName << resourceType << storageLocation;
546
547 QSqlQuery q;
548
549 if (!q.prepare("SELECT resources.id\n"
550 "FROM resources\n"
551 ", resource_types\n"
552 ", storages\n"
553 "WHERE resources.resource_type_id = resource_types.id\n"
554 "AND storages.id = resources.storage_id\n"
555 "AND storages.location = :storage_location\n"
556 "AND resource_types.name = :resource_type\n"
557 "AND resources.filename = :filename\n")) {
558 qWarning() << "Could not read and prepare resourceIdForResource" << q.lastError();
559 return -1;
560 }
561
562 q.bindValue(":filename", resourceFileName);
563 q.bindValue(":resource_type", resourceType);
564 q.bindValue(":storage_location", changeToEmptyIfNull(storageLocation));
565
566 if (!q.exec()) {
567 qWarning() << "Could not query resourceIdForResource" << q.boundValues() << q.lastError();
568 return -1;
569 }
570
571 if (q.first()) {
572 return q.value(0).toInt();
573 }
574
575 // couldn't be found in the `resources` table, but can still be in versioned_resources
576
577 if (!q.prepare("SELECT versioned_resources.resource_id\n"
578 "FROM resources\n"
579 ", resource_types\n"
580 ", versioned_resources\n"
581 ", storages\n"
582 "WHERE resources.resource_type_id = resource_types.id\n" // join resources and resource_types by resource id
583 "AND versioned_resources.resource_id = resources.id\n" // join versioned_resources and resources by resource id
584 "AND storages.id = versioned_resources.storage_id\n" // join storages and versioned_resources by storage id
585 "AND storages.location = :storage_location\n" // storage location must be the same as asked for
586 "AND resource_types.name = :resource_type\n" // resource type must be the same as asked for
587 "AND versioned_resources.filename = :filename\n")) { // filename must be the same as asked for
588 qWarning() << "Could not read and prepare resourceIdForResource (in versioned resources)" << q.lastError();
589 return -1;
590 }
591
592 q.bindValue(":filename", resourceFileName);
593 q.bindValue(":resource_type", resourceType);
594 q.bindValue(":storage_location", changeToEmptyIfNull(storageLocation));
595
596 if (!q.exec()) {
597 qWarning() << "Could not query resourceIdForResource (in versioned resources)" << q.boundValues() << q.lastError();
598 return -1;
599 }
600
601 if (q.first()) {
602 return q.value(0).toInt();
603 }
604
605 // commenting out, because otherwise it spams the console on every new resource in the local resources folder
606 // qWarning() << "Could not find resource" << resourceName << resourceFileName << resourceType << storageLocation;
607 return -1;
608
609}

References changeToEmptyIfNull().

◆ resourceNeedsUpdating()

bool KisResourceCacheDb::resourceNeedsUpdating ( int resourceId,
QDateTime timestamp )
staticprivate

Definition at line 611 of file KisResourceCacheDb.cpp.

612{
613 QSqlQuery q;
614 if (!q.prepare("SELECT timestamp\n"
615 "FROM versioned_resources\n"
616 "WHERE resource_id = :resource_id\n"
617 "AND version = (SELECT MAX(version)\n"
618 " FROM versioned_resources\n"
619 " WHERE resource_id = :resource_id);")) {
620 qWarning() << "Could not prepare resourceNeedsUpdating statement" << q.lastError();
621 return false;
622 }
623
624 q.bindValue(":resource_id", resourceId);
625
626 if (!q.exec()) {
627 qWarning() << "Could not query for the most recent timestamp" << q.boundValues() << q.lastError();
628 return false;
629 }
630
631 if (!q.first()) {
632 qWarning() << "Inconsistent database: could not find a version for resource with Id" << resourceId;
633 return false;
634 }
635
636 QVariant resourceTimeStamp = q.value(0);
637
638 if (!resourceTimeStamp.isValid()) {
639 qWarning() << "Could not retrieve timestamp from versioned_resources" << resourceId;
640 return false;
641 }
642
643 return (timestamp.toSecsSinceEpoch() > resourceTimeStamp.toInt());
644}

◆ resourcesForStorage()

QVector< int > KisResourceCacheDb::resourcesForStorage ( const QString & resourceType,
const QString & storageLocation )
staticprivate

Definition at line 509 of file KisResourceCacheDb.cpp.

510{
511 QVector<int> result;
512
513 QSqlQuery q;
514
515 if (!q.prepare("SELECT resources.id\n"
516 "FROM resources\n"
517 ", resource_types\n"
518 ", storages\n"
519 "WHERE resources.resource_type_id = resource_types.id\n"
520 "AND storages.id = resources.storage_id\n"
521 "AND storages.location = :storage_location\n"
522 "AND resource_types.name = :resource_type\n")) {
523
524 qWarning() << "Could not read and prepare resourcesForStorage" << q.lastError();
525 return result;
526 }
527
528 q.bindValue(":resource_type", resourceType);
529 q.bindValue(":storage_location", changeToEmptyIfNull(storageLocation));
530
531 if (!q.exec()) {
532 qWarning() << "Could not query resourceIdForResource" << q.boundValues() << q.lastError();
533 return result;
534 }
535
536 while (q.next()) {
537 result << q.value(0).toInt();
538 }
539
540 return result;
541}

References changeToEmptyIfNull().

◆ setForeignKeysStateImpl()

void KisResourceCacheDb::setForeignKeysStateImpl ( bool isEnabled)
static

set the foreign_keys feature state of the database (the function may throw SQL exceptions, call in a try-block only!)

Definition at line 2246 of file KisResourceCacheDb.cpp.

2247{
2248 KisSqlQueryLoader loader("inline://set_foreign_keys_state",
2249 QString("PRAGMA foreign_keys = %1").arg(isEnabled ? "ON" : "OFF"));
2250 loader.exec();
2251}

References KisSqlQueryLoader::exec().

◆ setResourceActive()

bool KisResourceCacheDb::setResourceActive ( int resourceId,
bool active = false )
staticprivate

Make this resource active or inactive; this does not remove the resource from disk or from the database.

Definition at line 1221 of file KisResourceCacheDb.cpp.

1222{
1223 if (resourceId < 0) {
1224 qWarning() << "Invalid resource id; cannot remove resource";
1225 return false;
1226 }
1227 QSqlQuery q;
1228 bool r = q.prepare("UPDATE resources\n"
1229 "SET status = :status\n"
1230 "WHERE id = :resource_id");
1231 if (!r) {
1232 qWarning() << "Could not prepare removeResource query" << q.lastError();
1233 }
1234 q.bindValue(":status", active);
1235 q.bindValue(":resource_id", resourceId);
1236 if (!q.exec()) {
1237 qWarning() << "Could not update resource" << resourceId << "to inactive" << q.lastError();
1238 return false;
1239 }
1240
1241 return true;
1242}

◆ synchronizeForeignKeysState()

void KisResourceCacheDb::synchronizeForeignKeysState ( )
static

Called in the end of the database creation step to enable or disable the foreign_keys state depending on the release status of Krita. Currently, only developer's builds of Krita have foreign_keys constraint enabled.

Definition at line 2267 of file KisResourceCacheDb.cpp.

2268{
2269#ifdef KRITA_STABLE
2270 bool useForeignKeys = false;
2271 KisUsageLogger::log("INFO: detected stable build of Krita, foreign_keys constraint will be disabled");
2272#else
2273 bool useForeignKeys = true;
2274 KisUsageLogger::log("INFO: detected unstable build of Krita, foreign_keys constraint will be enabled");
2275#endif
2276
2277 if (qEnvironmentVariableIsSet("KRITA_OVERRIDE_USE_FOREIGN_KEYS")) {
2278 useForeignKeys = qEnvironmentVariableIntValue("KRITA_OVERRIDE_USE_FOREIGN_KEYS") > 0;
2279 KisUsageLogger::log("INFO: foreign_keys constraint was overridden by KRITA_OVERRIDE_USE_FOREIGN_KEYS: " + QString::number(useForeignKeys));
2280 }
2281
2282 try {
2283 const bool oldForeignKeysState = KisResourceCacheDb::getForeignKeysStateImpl();
2284
2285 if (oldForeignKeysState != useForeignKeys) {
2287 "INFO: switch foreign_keys state: " +
2288 QString::number(oldForeignKeysState) +
2289 " -> " +
2290 QString::number(useForeignKeys));
2291
2293 }
2294
2295 } catch (const KisSqlQueryLoader::SQLException &e) {
2296 qWarning().noquote() << "ERROR: failed to execute query:" << e.message;
2297 qWarning().noquote() << " file:" << e.filePath;
2298 qWarning().noquote() << " statement:" << e.statementIndex;
2299 qWarning().noquote() << " error:" << e.sqlError.text();
2300 }
2301
2302}
static void setForeignKeysStateImpl(bool isEnabled)
static bool getForeignKeysStateImpl()
static void log(const QString &message)
Logs with date/time.

References KisSqlQueryLoader::SQLException::filePath, getForeignKeysStateImpl(), KisUsageLogger::log(), KisSqlQueryLoader::SQLException::message, setForeignKeysStateImpl(), KisSqlQueryLoader::SQLException::sqlError, and KisSqlQueryLoader::SQLException::statementIndex.

◆ synchronizeStorage()

bool KisResourceCacheDb::synchronizeStorage ( KisResourceStorageSP storage)
staticprivate

Start the transaction that will add all the resources

We compare resource versions one-by-one because the storage may have multiple versions of them

Firstly, fetch information about the existing resources in the storage

A fake resourceId to group resources which are not yet present in the database. This value is always negative, therefore it cannot overlap with normal ids.

Assign the detected resource id to all the versions of this resource (if they are not present in the database). If no id has been detected, then a fake one will be assigned.

Secondly, fetch the resources present in the database

The head of itA array contains some resources with fake (negative) resourceId. These resources are obviously new resources and should be added to the cache database.

Now both arrays are sorted in resourceId/version/timestamp order. It lets us easily find the resources that are unique to the storage or database. If *itA < *itB, then the resource is present in the storage only and should be added to the database. If *itA > *itB, then the resource is present in the database only and should be removed (because it has been removed from the storage);

In the main loop we modified the versioned_resource table only, now we should update the head resources table with the latest version of the resource (and upload the thumbnail as well)

Definition at line 1848 of file KisResourceCacheDb.cpp.

1849{
1850 QElapsedTimer t;
1851 t.start();
1852
1853 if (!s_valid) {
1854 qWarning() << "KisResourceCacheDb::addResource: The database is not valid";
1855 return false;
1856 }
1857
1858 bool success = true;
1859
1860 // Find the storage in the database
1861 QSqlQuery q;
1862 if (!q.prepare("SELECT id\n"
1863 ", timestamp\n"
1864 ", pre_installed\n"
1865 "FROM storages\n"
1866 "WHERE location = :location\n")) {
1867 qWarning() << "Could not prepare storage timestamp statement" << q.lastError();
1868 }
1869
1870 q.bindValue(":location", changeToEmptyIfNull(KisResourceLocator::instance()->makeStorageLocationRelative(storage->location())));
1871 if (!q.exec()) {
1872 qWarning() << "Could not execute storage timestamp statement" << q.boundValues() << q.lastError();
1873 }
1874
1875 if (!q.first()) {
1876 // This is a new storage, the user must have dropped it in the path before restarting Krita, so add it.
1877 debugResource << "Adding storage to the database:" << storage;
1878 if (!addStorage(storage, false)) {
1879 qWarning() << "Could not add new storage" << storage->name() << "to the database";
1880 success = false;
1881 }
1882 return success;
1883 }
1884
1885 storage->setStorageId(q.value("id").toInt());
1886
1888 QSqlDatabase::database().transaction();
1889
1892
1893 Q_FOREACH(const QString &resourceType, KisResourceLoaderRegistry::instance()->resourceTypes()) {
1894
1897
1898 QVector<ResourceVersion> resourcesInStorage;
1899
1903
1904 int nextInexistentResourceId = std::numeric_limits<int>::min();
1905
1906 QSharedPointer<KisResourceStorage::ResourceIterator> iter = storage->resources(resourceType);
1907 while (iter->hasNext()) {
1908 iter->next();
1909
1910 const int firstResourceVersionPosition = resourcesInStorage.size();
1911
1912 int detectedResourceId = nextInexistentResourceId;
1914 iter->versions();
1915
1916 while (verIt->hasNext()) {
1917 verIt->next();
1918
1919 // verIt->url() contains paths like "brushes/ink.png" or "brushes/subfolder/splash.png".
1920 // we need to cut off the first part and get "ink.png" in the first case,
1921 // but "subfolder/splash.png" in the second case in order for subfolders to work
1922 // so it cannot just use QFileInfo(verIt->url()).fileName() here.
1923 QString path = QDir::fromNativeSeparators(verIt->url()); // make sure it uses Unix separators
1924 int folderEndIdx = path.indexOf("/");
1925 QString properFilenameWithSubfolders = path.right(path.length() - folderEndIdx - 1);
1926 int id = resourceIdForResource(properFilenameWithSubfolders,
1927 verIt->type(),
1929
1930 ResourceVersion item;
1931 item.url = verIt->url();
1932 item.version = verIt->guessedVersion();
1933
1934 // we use lower precision than the normal QDateTime
1935 item.timestamp = QDateTime::fromSecsSinceEpoch(verIt->lastModified().toSecsSinceEpoch());
1936
1937 item.resourceId = id;
1938
1939 if (detectedResourceId < 0 && id >= 0) {
1940 detectedResourceId = id;
1941 }
1942
1943 resourcesInStorage.append(item);
1944 }
1945
1949
1950 for (int i = firstResourceVersionPosition; i < resourcesInStorage.size(); i++) {
1951 if (resourcesInStorage[i].resourceId < 0) {
1952 resourcesInStorage[i].resourceId = detectedResourceId;
1953 }
1954 }
1955
1956 nextInexistentResourceId++;
1957 }
1958
1959
1961
1962 QVector<ResourceVersion> resourcesInDatabase;
1963
1964 QSqlQuery q;
1965 q.setForwardOnly(true);
1966 if (!q.prepare("SELECT versioned_resources.resource_id, versioned_resources.filename, versioned_resources.version, versioned_resources.timestamp\n"
1967 "FROM versioned_resources\n"
1968 ", resource_types\n"
1969 ", resources\n"
1970 "WHERE resources.resource_type_id = resource_types.id\n"
1971 "AND resources.id = versioned_resources.resource_id\n"
1972 "AND resource_types.name = :resource_type\n"
1973 "AND versioned_resources.storage_id == :storage_id")) {
1974 qWarning() << "Could not prepare resource by type query" << q.lastError();
1975 success = false;
1976 continue;
1977 }
1978
1979 q.bindValue(":resource_type", resourceType);
1980 q.bindValue(":storage_id", int(storage->storageId()));
1981
1982 if (!q.exec()) {
1983 qWarning() << "Could not exec resource by type query" << q.boundValues() << q.lastError();
1984 success = false;
1985 continue;
1986 }
1987
1988 while (q.next()) {
1989 ResourceVersion item;
1990 item.url = resourceType + "/" + q.value(1).toString();
1991 item.version = q.value(2).toInt();
1992 item.timestamp = QDateTime::fromSecsSinceEpoch(q.value(3).toInt());
1993 item.resourceId = q.value(0).toInt();
1994
1995 resourcesInDatabase.append(item);
1996 }
1997
1998 QSet<int> resourceIdForUpdate;
1999
2000 std::sort(resourcesInStorage.begin(), resourcesInStorage.end());
2001 std::sort(resourcesInDatabase.begin(), resourcesInDatabase.end());
2002
2003 auto itA = resourcesInStorage.begin();
2004 auto endA = resourcesInStorage.end();
2005
2006 auto itB = resourcesInDatabase.begin();
2007 auto endB = resourcesInDatabase.end();
2008
2012
2013 while (itA != endA) {
2014 if (itA->resourceId >= 0) break;
2015
2016 KoResourceSP res = storage->resource(itA->url);
2017
2018 if (!res) {
2019 KisUsageLogger::log("Could not load resource " + itA->url);
2020 ++itA;
2021 continue;
2022 }
2023
2024 res->setVersion(itA->version);
2025 res->setMD5Sum(storage->resourceMd5(itA->url));
2026 if (!res->valid()) {
2027 KisUsageLogger::log("Could not retrieve md5 for resource " + itA->url);
2028 ++itA;
2029 continue;
2030 }
2031
2032 const bool retval = addResource(storage, itA->timestamp, res, resourceType);
2033 if (!retval) {
2034 KisUsageLogger::log("Could not add resource " + itA->url);
2035 ++itA;
2036 continue;
2037 }
2038
2039 const int resourceId = res->resourceId();
2040 KIS_SAFE_ASSERT_RECOVER(resourceId >= 0) {
2041 KisUsageLogger::log("Could not get id for resource " + itA->url);
2042 ++itA;
2043 continue;
2044 }
2045
2046 auto nextResource = std::upper_bound(itA, endA, *itA, ResourceVersion::CompareByResourceId());
2047 for (auto it = std::next(itA); it != nextResource; ++it) {
2048 KoResourceSP res = storage->resource(it->url);
2049 res->setVersion(it->version);
2050 res->setMD5Sum(storage->resourceMd5(it->url));
2051 if (!res->valid()) {
2052 continue;
2053 }
2054
2055 const bool retval = addResourceVersion(resourceId, it->timestamp, storage, res);
2056 KIS_SAFE_ASSERT_RECOVER(retval) {
2057 KisUsageLogger::log("Could not add version for resource " + itA->url);
2058 continue;
2059 }
2060 }
2061
2062 itA = nextResource;
2063 }
2064
2072
2073 while (itA != endA || itB != endB) {
2074 if ((itA != endA && itB != endB && *itA < *itB) ||
2075 itB == endB) {
2076
2077 // add a version to the database
2078
2079 KoResourceSP res = storage->resource(itA->url);
2080 if (res) {
2081 res->setVersion(itA->version);
2082 res->setMD5Sum(storage->resourceMd5(itA->url));
2083
2084 const bool result = addResourceVersionImpl(itA->resourceId, itA->timestamp, storage, res);
2086
2087 resourceIdForUpdate.insert(itA->resourceId);
2088 }
2089 ++itA;
2090
2091 } else if ((itA != endA && itB != endB && *itA > *itB) ||
2092 itA == endA) {
2093
2094 // remove a version from the database
2095 const bool result = removeResourceVersionImpl(itB->resourceId, itB->version, storage);
2097 resourceIdForUpdate.insert(itB->resourceId);
2098 ++itB;
2099
2100 } else {
2101 // resources are equal, just skip them
2102 ++itA;
2103 ++itB;
2104 }
2105 }
2106
2107
2111
2112 for (auto it = resourceIdForUpdate.begin(); it != resourceIdForUpdate.end(); ++it) {
2113 updateResourceTableForResourceIfNeeded(*it, resourceType, storage);
2114 }
2115 }
2116
2117 QSqlDatabase::database().commit();
2118 debugResource << "Synchronizing the storages took" << t.elapsed() << "milliseconds for" << storage->location();
2119
2120 return success;
2121}
static bool addStorage(KisResourceStorageSP storage, bool preinstalled)
static bool removeResourceVersionImpl(int resourceId, int version, KisResourceStorageSP storage)
static bool updateResourceTableForResourceIfNeeded(int resourceId, const QString &resourceType, KisResourceStorageSP storage)
#define KIS_SAFE_ASSERT_RECOVER(cond)
Definition kis_assert.h:126

References addResource(), addResourceVersion(), addResourceVersionImpl(), addStorage(), changeToEmptyIfNull(), debugResource, KisResourceLoaderRegistry::instance(), KisResourceLocator::instance(), KIS_SAFE_ASSERT_RECOVER, KIS_SAFE_ASSERT_RECOVER_NOOP, KisUsageLogger::log(), KisResourceLocator::makeStorageLocationRelative(), removeResourceVersionImpl(), resourceIdForResource(), s_valid, and updateResourceTableForResourceIfNeeded().

◆ tagResource()

bool KisResourceCacheDb::tagResource ( const QString & resourceFileName,
KisTagSP tag,
const QString & resourceType )
staticprivate

Definition at line 1244 of file KisResourceCacheDb.cpp.

1245{
1246 // Get tag id
1247 int tagId {-1};
1248 {
1249 QFile f(":/select_tag.sql");
1250 if (f.open(QFile::ReadOnly)) {
1251 QSqlQuery q;
1252 if (!q.prepare(f.readAll())) {
1253 qWarning() << "Could not read and prepare select_tag.sql" << q.lastError();
1254 return false;
1255 }
1256 q.bindValue(":url", tag->url());
1257 q.bindValue(":resource_type", resourceType);
1258
1259 if (!q.exec()) {
1260 qWarning() << "Could not query tags" << q.boundValues() << q.lastError();
1261 return false;
1262 }
1263
1264 if (!q.first()) {
1265 qWarning() << "Could not find tag" << q.boundValues() << q.lastError();
1266 return false;
1267 }
1268
1269 tagId = q.value(0).toInt();
1270 }
1271 }
1272
1273
1274 // Get resource id
1275 QSqlQuery q;
1276 bool r = q.prepare("SELECT resources.id\n"
1277 "FROM resources\n"
1278 ", resource_types\n"
1279 "WHERE resources.resource_type_id = resource_types.id\n"
1280 "AND resource_types.name = :resource_type\n"
1281 "AND resources.filename = :resource_filename\n");
1282 if (!r) {
1283 qWarning() << "Could not prepare tagResource query" << q.lastError();
1284 return false;
1285 }
1286
1287 q.bindValue(":resource_type", resourceType);
1288 q.bindValue(":resource_filename", resourceFileName);
1289
1290 if (!q.exec()) {
1291 qWarning() << "Could not execute tagResource statement" << q.boundValues() << q.lastError();
1292 return false;
1293 }
1294
1295
1296 while (q.next()) {
1297
1298 int resourceId = q.value(0).toInt();
1299
1300 if (resourceId < 0) {
1301 qWarning() << "Could not find resource to tag" << resourceFileName << resourceType;
1302 continue;
1303 }
1304
1305 {
1306 QSqlQuery q;
1307 if (!q.prepare("SELECT COUNT(*)\n"
1308 "FROM resource_tags\n"
1309 "WHERE resource_id = :resource_id\n"
1310 "AND tag_id = :tag_id")) {
1311 qWarning() << "Could not prepare tagResource query 2" << q.lastError();
1312 continue;
1313 }
1314 q.bindValue(":resource_id", resourceId);
1315 q.bindValue(":tag_id", tagId);
1316
1317 if (!q.exec()) {
1318 qWarning() << "Could not execute tagResource query 2" << q.lastError() << q.boundValues();
1319 continue;
1320 }
1321
1322 q.first();
1323 int count = q.value(0).toInt();
1324 if (count > 0) {
1325 continue;
1326 }
1327 }
1328
1329 {
1330 QSqlQuery q;
1331 if (!q.prepare("INSERT INTO resource_tags\n"
1332 "(resource_id, tag_id)\n"
1333 "VALUES\n"
1334 "(:resource_id, :tag_id);")) {
1335 qWarning() << "Could not prepare tagResource insert statement" << q.lastError();
1336 continue;
1337 }
1338
1339 q.bindValue(":resource_id", resourceId);
1340 q.bindValue(":tag_id", tagId);
1341
1342 if (!q.exec()) {
1343 qWarning() << "Could not execute tagResource stagement" << q.boundValues() << q.lastError();
1344 continue;
1345 }
1346 }
1347 }
1348 return true;
1349}

◆ updateMetaDataForId()

bool KisResourceCacheDb::updateMetaDataForId ( const QMap< QString, QVariant > map,
int id,
const QString & tableName )
staticprivate

setMetaDataForId removes all metadata for the given id and table name, and inserts the metadata in the metadata table.

Parameters
id
tableName
Returns
true if successful, false if not

Definition at line 2380 of file KisResourceCacheDb.cpp.

2381{
2382 QSqlDatabase::database().transaction();
2383
2384 {
2385 QSqlQuery q;
2386 if (!q.prepare("DELETE FROM metadata\n"
2387 "WHERE foreign_id = :id\n"
2388 "AND table_name = :table\n")) {
2389 QSqlDatabase::database().rollback();
2390 qWarning() << "Could not prepare delete metadata query" << q.lastError();
2391 return false;
2392 }
2393
2394 q.bindValue(":id", id);
2395 q.bindValue(":table", tableName);
2396
2397 if (!q.exec()) {
2398 QSqlDatabase::database().rollback();
2399 qWarning() << "Could not execute delete metadata query" << q.lastError();
2400 return false;
2401
2402 }
2403 }
2404
2405 if (addMetaDataForId(map, id, tableName)) {
2406 QSqlDatabase::database().commit();
2407 }
2408 else {
2409 QSqlDatabase::database().rollback();
2410 }
2411 return true;
2412}
static bool addMetaDataForId(const QMap< QString, QVariant > map, int id, const QString &tableName)

References addMetaDataForId().

◆ updateResourceTableForResourceIfNeeded()

bool KisResourceCacheDb::updateResourceTableForResourceIfNeeded ( int resourceId,
const QString & resourceType,
KisResourceStorageSP storage )
staticprivate

Definition at line 743 of file KisResourceCacheDb.cpp.

744{
745 bool r = false;
746
747 int maxVersion = -1;
748 {
749 QSqlQuery q;
750 r = q.prepare("SELECT MAX(version)\n"
751 "FROM versioned_resources\n"
752 "WHERE resource_id = :resource_id;");
753 if (!r) {
754 qWarning() << "Could not prepare findMaxVersion statement" << q.lastError();
755 return r;
756 }
757
758 q.bindValue(":resource_id", resourceId);
759
760 r = q.exec();
761 if (!r) {
762 qWarning() << "Could not execute findMaxVersion query" << q.boundValues() << q.lastError();
763 return r;
764 }
765
766 r = q.first();
768
769 maxVersion = q.value(0).toInt();
770 }
771
772 QString maxVersionFilename;
773 {
774 QSqlQuery q;
775 r = q.prepare("SELECT filename\n"
776 "FROM versioned_resources\n"
777 "WHERE resource_id = :resource_id\n"
778 "AND version = :version;");
779 if (!r) {
780 qWarning() << "Could not prepare findMaxVersionFilename statement" << q.lastError();
781 return r;
782 }
783
784 q.bindValue(":resource_id", resourceId);
785 q.bindValue(":version", maxVersion);
786
787 r = q.exec();
788 if (!r) {
789 qWarning() << "Could not execute findMaxVersionFilename query" << q.boundValues() << q.lastError();
790 return r;
791 }
792
793 if (!q.first()) {
794 return removeResourceCompletely(resourceId);
795 } else {
796 maxVersionFilename = q.value(0).toString();
797 }
798 }
799
800 QString currentFilename;
801 {
802 QSqlQuery q;
803 r = q.prepare("SELECT filename\n"
804 "FROM resources\n"
805 "WHERE id = :resource_id;");
806 if (!r) {
807 qWarning() << "Could not prepare findMaxVersion statement" << q.lastError();
808 return r;
809 }
810
811 q.bindValue(":resource_id", resourceId);
812
813 r = q.exec();
814 if (!r) {
815 qWarning() << "Could not execute findMaxVersion query" << q.boundValues() << q.lastError();
816 return r;
817 }
818
819 r = q.first();
821
822 currentFilename = q.value(0).toString();
823 }
824
825 if (currentFilename != maxVersionFilename) {
826 const QString url = resourceType + "/" + maxVersionFilename;
827 KoResourceSP resource = storage->resource(url);
829 resource->setVersion(maxVersion);
830 resource->setMD5Sum(storage->resourceMd5(url));
831 resource->setStorageLocation(storage->location());
832 r = makeResourceTheCurrentVersion(resourceId, resource);
833 }
834
835 return r;
836}
static bool removeResourceCompletely(int resourceId)
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129

References KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE, makeResourceTheCurrentVersion(), and removeResourceCompletely().

Friends And Related Symbol Documentation

◆ KisAllResourcesModel

friend class KisAllResourcesModel
friend

Definition at line 83 of file KisResourceCacheDb.h.

◆ KisAllTagsModel

friend class KisAllTagsModel
friend

Definition at line 79 of file KisResourceCacheDb.h.

◆ KisDocument

friend class KisDocument
friend

Definition at line 82 of file KisResourceCacheDb.h.

◆ KisResourceLoaderRegistry

friend class KisResourceLoaderRegistry
friend

Definition at line 80 of file KisResourceCacheDb.h.

◆ KisResourceLocator

friend class KisResourceLocator
friend

Definition at line 76 of file KisResourceCacheDb.h.

◆ KisResourceUserOperations

friend class KisResourceUserOperations
friend

Definition at line 81 of file KisResourceCacheDb.h.

◆ TestResourceCacheDb

friend class TestResourceCacheDb
friend

Definition at line 78 of file KisResourceCacheDb.h.

◆ TestResourceLocator

friend class TestResourceLocator
friend

Definition at line 77 of file KisResourceCacheDb.h.

Member Data Documentation

◆ databaseVersion

const QString KisResourceCacheDb::databaseVersion { "0.0.18" }
static

current schema version

Definition at line 46 of file KisResourceCacheDb.h.

74:
75
76 friend class KisResourceLocator;
77 friend class TestResourceLocator;
78 friend class TestResourceCacheDb;
79 friend class KisAllTagsModel;
80 friend class KisResourceLoaderRegistry;
81 friend class KisResourceUserOperations;
82 friend class KisDocument;
83 friend class KisAllResourcesModel;
84
85 explicit KisResourceCacheDb(); // Deleted
86 ~KisResourceCacheDb(); // Deleted
93 static bool registerResourceType(const QString &resourceType);
94
95 static QVector<int> resourcesForStorage(const QString &resourceType, const QString &storageLocation);
96 static int resourceIdForResource(const QString &resourceFileName, const QString &resourceType, const QString &storageLocation);
97 static bool resourceNeedsUpdating(int resourceId, QDateTime timestamp);
98
108 static bool addResourceVersion(int resourceId, QDateTime timestamp, KisResourceStorageSP storage, KoResourceSP resource);
109
110 static bool addResourceVersionImpl(int resourceId, QDateTime timestamp, KisResourceStorageSP storage, KoResourceSP resource);
111 static bool removeResourceVersionImpl(int resourceId, int version, KisResourceStorageSP storage);
112
113 static bool updateResourceTableForResourceIfNeeded(int resourceId, const QString &resourceType, KisResourceStorageSP storage);
114 static bool makeResourceTheCurrentVersion(int resourceId, KoResourceSP resource);
115 static bool removeResourceCompletely(int resourceId);
116
118 static bool getResourceIdFromFilename(QString filename, QString resourceType, QString storageLocation, int &outResourceId);
120 static bool getResourceIdFromVersionedFilename(QString filename, QString resourceType, QString storageLocation, int& outResourceId);
121 static bool getAllVersionsLocations(int resourceId, QStringList &outVersionsLocationsList);
122
123
124 static bool addResource(KisResourceStorageSP storage, QDateTime timestamp, KoResourceSP resource, const QString &resourceType);
125 static bool addResources(KisResourceStorageSP storage, QString resourceType);
126
128 static bool setResourceActive(int resourceId, bool active = false);
129
130 static bool tagResource(const QString &resourceFileName, KisTagSP tag, const QString &resourceType);
131 static bool hasTag(const QString &url, const QString &resourceType);
132 static bool linkTagToStorage(const QString &url, const QString &resourceType, const QString &storageLocation);
133 static bool addTag(const QString &resourceType, const QString storageLocation, KisTagSP tag);
134 static bool addTags(KisResourceStorageSP storage, QString resourceType);
135
141 static bool registerStorageType(const KisResourceStorage::StorageType storageType);
142 static bool addStorage(KisResourceStorageSP storage, bool preinstalled);
143 static bool addStorageTags(KisResourceStorageSP storage);
144
146 static bool deleteStorage(KisResourceStorageSP storage);
149 static bool deleteStorage(QString location);
150 static bool synchronizeStorage(KisResourceStorageSP storage);
151
158 static QMap<QString, QVariant> metaDataForId(int id, const QString &tableName);
159
167 static bool updateMetaDataForId(const QMap<QString, QVariant> map, int id, const QString &tableName);
168 static bool addMetaDataForId(const QMap<QString, QVariant> map, int id, const QString &tableName);
169
176 static bool removeOrphanedMetaData();
177
178 static bool s_valid;
179 static QString s_lastError;
180};
181
182#endif // KISRESOURCECACHEDB_H
The KisAllresourcesModel class provides access to the cache database for a particular resource type....
The KisResourceCacheDb class encapsulates the database that caches information about the resources av...
static bool resourceNeedsUpdating(int resourceId, QDateTime timestamp)
static bool getAllVersionsLocations(int resourceId, QStringList &outVersionsLocationsList)
KisResourceCacheDb operator=(const KisResourceCacheDb &)
static bool removeOrphanedMetaData()
removeOrphanedMetaData Previous versions of Krita never removed metadata, so this function doublechec...
static QMap< QString, QVariant > metaDataForId(int id, const QString &tableName)
metaDataForId
static QVector< int > resourcesForStorage(const QString &resourceType, const QString &storageLocation)
static bool getResourceIdFromFilename(QString filename, QString resourceType, QString storageLocation, int &outResourceId)
The function will find the resource only if it is the latest version.
static bool setResourceActive(int resourceId, bool active=false)
Make this resource active or inactive; this does not remove the resource from disk or from the databa...
static bool addStorageTags(KisResourceStorageSP storage)
friend class TestResourceLocator
static bool getResourceIdFromVersionedFilename(QString filename, QString resourceType, QString storageLocation, int &outResourceId)
Note that here you can put even the original filename - any filename from the versioned_resources - a...
friend class TestResourceCacheDb
static bool registerResourceType(const QString &resourceType)
registerResourceType registers this resource type in the database
static bool synchronizeStorage(KisResourceStorageSP storage)
static bool registerStorageType(const KisResourceStorage::StorageType storageType)
registerStorageType registers this storage type in the database
The KisResourceLoaderRegistry class manages the loader plugins for resources. Every resource can be l...

◆ disabledBundles

QStringList KisResourceCacheDb::disabledBundles { QStringList() << "Krita_3_Default_Resources.bundle" }
static

the list of compatibility bundles that need to inactive by default

Definition at line 48 of file KisResourceCacheDb.h.

◆ resourceCacheDbFilename

const QString KisResourceCacheDb::resourceCacheDbFilename { "resourcecache.sqlite" }
static

filename of the database

Definition at line 45 of file KisResourceCacheDb.h.

◆ s_lastError

QString KisResourceCacheDb::s_lastError {QString()}
staticprivate

Definition at line 51 of file KisResourceCacheDb.h.

◆ s_valid

bool KisResourceCacheDb::s_valid {false}
staticprivate

Definition at line 50 of file KisResourceCacheDb.h.

◆ storageTypes

QStringList KisResourceCacheDb::storageTypes { QStringList() }
static

kinds of places where resources can be stored

Definition at line 47 of file KisResourceCacheDb.h.


The documentation for this class was generated from the following files: