10#include <QSqlDatabase>
13#include <QVersionNumber>
14#include <QStandardPaths>
16#include <QDirIterator>
18#include <QElapsedTimer>
25#include <klocalizedstring.h>
69 return s.isNull() ? QString(
"") : s;
74 QFile f(
":/fill_version_information.sql");
75 if (f.open(QFile::ReadOnly)) {
76 QString sql = f.readAll();
78 if (!q.prepare(sql)) {
79 warnDbMigration <<
"Could not prepare the schema information query" << q.lastError() << q.boundValues();
84 q.addBindValue(QDateTime::currentDateTimeUtc().toSecsSinceEpoch());
86 warnDbMigration <<
"Could not insert the current version" << q.lastError() << q.boundValues();
103 warnDbMigration.noquote() <<
"ERROR: Could not execute DB update step:" << message;
108 QSqlError(
"Error executing SQL",
109 QString(
"Could not find SQL file %1").arg(e.
filePath),
110 QSqlError::StatementError);
112 warnDbMigration.noquote() <<
"ERROR: Could not execute DB update step:" << message;
132 warnDbMigration.noquote() <<
"ERROR: Could execute DB update step:" << message;
156 QDir dbLocation(location);
157 if (!dbLocation.exists()) {
158 dbLocation.mkpath(dbLocation.path());
161 std::optional<QSqlDatabase> existingDatabase =
162 QSqlDatabase::database(QSqlDatabase::defaultConnection,
false);
164 const bool databaseConnectionExists = !QSqlDatabase::connectionNames().isEmpty()
165 && existingDatabase->isValid() && existingDatabase->isOpen();
167 if (databaseConnectionExists && existingDatabase->tables().contains(
"version_information")) {
171 existingDatabase = std::nullopt;
175 if (!databaseConnectionExists) {
176 db = QSqlDatabase::addDatabase(
dbDriver);
181 return db.lastError();
184 db = QSqlDatabase::database();
188 QVersionNumber oldSchemaVersionNumber;
198 <<
"versioned_resources"
202 <<
"tag_translations";
207 bool allTablesPresent =
true;
208 dbTables = db.tables();
209 Q_FOREACH(
const QString &table, tables) {
210 if (!dbTables.contains(table)) {
211 allTablesPresent =
false;
216 bool schemaIsOutDated =
false;
217 QString schemaVersion =
"0.0.0";
218 QString kritaVersion =
"Unknown";
219 int creationDate = 0;
221 if (dbTables.contains(
"version_information")) {
226 "SELECT database_version\n"
229 "FROM version_information\n"
235 warnDbMigration <<
"Could not retrieve version information from the database." << q.lastError();
239 schemaVersion = q.value(0).toString();
240 kritaVersion = q.value(1).toString();
241 creationDate = q.value(2).toInt();
244 oldSchemaVersionNumber = QVersionNumber::fromString(schemaVersion);
247 if (QVersionNumber::compare(oldSchemaVersionNumber, newSchemaVersionNumber) != 0) {
249 infoDbMigration <<
"Old schema:" << schemaVersion <<
"New schema:" << newSchemaVersionNumber;
251 schemaIsOutDated =
true;
254 if (newSchemaVersionNumber == QVersionNumber::fromString(
"0.0.18")
255 && QVersionNumber::compare(oldSchemaVersionNumber, QVersionNumber::fromString(
"0.0.14")) >= 0
256 && QVersionNumber::compare(oldSchemaVersionNumber, QVersionNumber::fromString(
"0.0.18")) < 0) {
258 bool from14to15 = oldSchemaVersionNumber == QVersionNumber::fromString(
"0.0.14");
260 bool from15to16 = oldSchemaVersionNumber == QVersionNumber::fromString(
"0.0.14")
261 || oldSchemaVersionNumber == QVersionNumber::fromString(
"0.0.15");
263 bool from16to17 = oldSchemaVersionNumber == QVersionNumber::fromString(
"0.0.14")
264 || oldSchemaVersionNumber == QVersionNumber::fromString(
"0.0.15")
265 || oldSchemaVersionNumber == QVersionNumber::fromString(
"0.0.16");
267 bool from17to18 = oldSchemaVersionNumber == QVersionNumber::fromString(
"0.0.14")
268 || oldSchemaVersionNumber == QVersionNumber::fromString(
"0.0.15")
269 || oldSchemaVersionNumber == QVersionNumber::fromString(
"0.0.16")
270 || oldSchemaVersionNumber == QVersionNumber::fromString(
"0.0.17");
277 "ALTER TABLE resource_tags\n"
278 "ADD COLUMN active INTEGER NOT NULL DEFAULT 1",
279 "Update resource tags table (add \'active\' column)");
280 if (error.type() != QSqlError::NoError) {
284 if (success && from15to16) {
289 Q_FOREACH(
const QString &index, indexes) {
291 QString(
"Create index for %1").arg(index));
292 if (error.type() != QSqlError::NoError) {
298 if (success && from16to17) {
300 "Create index for resources_signature");
301 if (error.type() != QSqlError::NoError) {
306 if (success && from17to18) {
309 "Cleanup and deduplicate metadata table");
310 if (error.type() != QSqlError::NoError) {
315 QSqlError error =
runUpdateScriptFile(
":/0_0_18_0002_update_metadata_table_constraints.sql",
316 "Update metadata table constraints");
317 if (error.type() != QSqlError::NoError) {
323 "Create index for metadata_key");
324 if (error.type() != QSqlError::NoError) {
339 "Vacuum database after updating schema");
340 if (error.type() != QSqlError::NoError) {
348 schemaIsOutDated = !success;
352 if (schemaIsOutDated) {
353 QMessageBox::critical(0, i18nc(
"@title:window",
"Krita"), i18n(
"The resource database scheme has changed. Krita will backup your database and create a new database."));
354 if (QVersionNumber::compare(oldSchemaVersionNumber, QVersionNumber::fromString(
"0.0.14")) > 0) {
365 if (allTablesPresent && !schemaIsOutDated) {
366 KisUsageLogger::log(QString(
"Database is up to date. Version: %1, created by Krita %2, at %3")
369 .arg(QDateTime::fromSecsSinceEpoch(creationDate).toString()));
380 .arg(oldSchemaVersionNumber.toString().isEmpty() ? QString(
"database didn't exist") : (
"old schema version: " + oldSchemaVersionNumber.toString()))
381 .arg(
"new schema version: " + newSchemaVersionNumber.toString()));
386 Q_FOREACH(
const QString &table, tables) {
389 if (error.type() != QSqlError::NoError) {
396 QSqlError error =
runUpdateScriptFile(
":/0_0_18_0002_update_metadata_table_constraints.sql",
397 "Update metadata table constraints");
399 if (error.type() != QSqlError::NoError) {
408 indexes <<
"storages" <<
"versioned_resources" <<
"tags" <<
"resources" <<
"tag_translations" <<
"resource_tags";
411 indexes <<
"resources_signature";
414 indexes <<
"metadata_key";
416 Q_FOREACH(
const QString &index, indexes) {
418 QString(
"Create index for %1").arg(index));
419 if (error.type() != QSqlError::NoError) {
426 QFile f(
":/fill_storage_types.sql");
427 if (f.open(QFile::ReadOnly)) {
428 QString sql = f.readAll();
430 const QString updateStep = QString(
"Register storage type: %1").arg(originType);
432 q.addBindValue(originType);
434 warnDbMigration <<
"Could execute DB update step:" << updateStep << q.lastError();
436 return db.lastError();
442 return QSqlError(
"Error executing SQL", QString(
"Could not find SQL fill_storage_types.sql."), QSqlError::StatementError);
447 QFile f(
":/fill_resource_types.sql");
448 if (f.open(QFile::ReadOnly)) {
449 QString sql = f.readAll();
451 const QString updateStep = QString(
"Register resource type: %1").arg(resourceType);
453 q.addBindValue(resourceType);
455 warnDbMigration <<
"Could execute DB update step:" << updateStep << q.lastError();
457 return db.lastError();
463 return QSqlError(
"Error executing SQL", QString(
"Could not find SQL fill_resource_types.sql."), QSqlError::StatementError);
468 return QSqlError(
"Error executing SQL", QString(
"Could not update schema version."), QSqlError::StatementError);
485 switch (err.type()) {
486 case QSqlError::NoError:
489 case QSqlError::ConnectionError:
490 s_lastError = QString(
"Could not initialize the resource cache database. Connection error: %1").arg(err.text());
492 case QSqlError::StatementError:
493 s_lastError = QString(
"Could not initialize the resource cache database. Statement error: %1").arg(err.text());
495 case QSqlError::TransactionError:
496 s_lastError = QString(
"Could not initialize the resource cache database. Transaction error: %1").arg(err.text());
498 case QSqlError::UnknownError:
499 s_lastError = QString(
"Could not initialize the resource cache database. Unknown error: %1").arg(err.text());
515 if (!q.prepare(
"SELECT resources.id\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")) {
524 qWarning() <<
"Could not read and prepare resourcesForStorage" << q.lastError();
528 q.bindValue(
":resource_type", resourceType);
532 qWarning() <<
"Could not query resourceIdForResource" << q.boundValues() << q.lastError();
537 result << q.value(0).toInt();
549 if (!q.prepare(
"SELECT resources.id\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();
562 q.bindValue(
":filename", resourceFileName);
563 q.bindValue(
":resource_type", resourceType);
567 qWarning() <<
"Could not query resourceIdForResource" << q.boundValues() << q.lastError();
572 return q.value(0).toInt();
577 if (!q.prepare(
"SELECT versioned_resources.resource_id\n"
580 ", versioned_resources\n"
582 "WHERE resources.resource_type_id = resource_types.id\n"
583 "AND versioned_resources.resource_id = resources.id\n"
584 "AND storages.id = versioned_resources.storage_id\n"
585 "AND storages.location = :storage_location\n"
586 "AND resource_types.name = :resource_type\n"
587 "AND versioned_resources.filename = :filename\n")) {
588 qWarning() <<
"Could not read and prepare resourceIdForResource (in versioned resources)" << q.lastError();
592 q.bindValue(
":filename", resourceFileName);
593 q.bindValue(
":resource_type", resourceType);
597 qWarning() <<
"Could not query resourceIdForResource (in versioned resources)" << q.boundValues() << q.lastError();
602 return q.value(0).toInt();
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();
624 q.bindValue(
":resource_id", resourceId);
627 qWarning() <<
"Could not query for the most recent timestamp" << q.boundValues() << q.lastError();
632 qWarning() <<
"Inconsistent database: could not find a version for resource with Id" << resourceId;
636 QVariant resourceTimeStamp = q.value(0);
638 if (!resourceTimeStamp.isValid()) {
639 qWarning() <<
"Could not retrieve timestamp from versioned_resources" << resourceId;
643 return (timestamp.toSecsSinceEpoch() > resourceTimeStamp.toInt());
671 Q_ASSERT(resource->version() >= 0);
674 r = q.prepare(
"INSERT INTO versioned_resources \n"
675 "(resource_id, storage_id, version, filename, timestamp, md5sum)\n"
680 " WHERE location = :storage_location)\n"
688 qWarning() <<
"Could not prepare addResourceVersion statement" << q.lastError();
692 q.bindValue(
":resource_id", resourceId);
694 q.bindValue(
":version", resource->version());
695 q.bindValue(
":filename", resource->filename());
696 q.bindValue(
":timestamp", timestamp.toSecsSinceEpoch());
698 q.bindValue(
":md5sum", resource->md5Sum());
702 qWarning() <<
"Could not execute addResourceVersionImpl statement" << q.lastError() << resourceId << storage->name() << storage->location() << resource->name() << resource->filename() <<
"version" << resource->version();
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"
723 " WHERE location = :storage_location);");
726 qWarning() <<
"Could not prepare removeResourceVersionImpl statement" << q.lastError();
730 q.bindValue(
":resource_id", resourceId);
732 q.bindValue(
":version", version);
736 qWarning() <<
"Could not execute removeResourceVersionImpl statement" << q.lastError() << resourceId << storage->name() << storage->location() <<
"version" << version;
750 r = q.prepare(
"SELECT MAX(version)\n"
751 "FROM versioned_resources\n"
752 "WHERE resource_id = :resource_id;");
754 qWarning() <<
"Could not prepare findMaxVersion statement" << q.lastError();
758 q.bindValue(
":resource_id", resourceId);
762 qWarning() <<
"Could not execute findMaxVersion query" << q.boundValues() << q.lastError();
769 maxVersion = q.value(0).toInt();
772 QString maxVersionFilename;
775 r = q.prepare(
"SELECT filename\n"
776 "FROM versioned_resources\n"
777 "WHERE resource_id = :resource_id\n"
778 "AND version = :version;");
780 qWarning() <<
"Could not prepare findMaxVersionFilename statement" << q.lastError();
784 q.bindValue(
":resource_id", resourceId);
785 q.bindValue(
":version", maxVersion);
789 qWarning() <<
"Could not execute findMaxVersionFilename query" << q.boundValues() << q.lastError();
796 maxVersionFilename = q.value(0).toString();
800 QString currentFilename;
803 r = q.prepare(
"SELECT filename\n"
805 "WHERE id = :resource_id;");
807 qWarning() <<
"Could not prepare findMaxVersion statement" << q.lastError();
811 q.bindValue(
":resource_id", resourceId);
815 qWarning() <<
"Could not execute findMaxVersion query" << q.boundValues() << q.lastError();
822 currentFilename = q.value(0).toString();
825 if (currentFilename != maxVersionFilename) {
826 const QString url = resourceType +
"/" + maxVersionFilename;
829 resource->setVersion(maxVersion);
830 resource->setMD5Sum(storage->resourceMd5(url));
831 resource->setStorageLocation(storage->location());
843 r = q.prepare(
"UPDATE resources\n"
845 ", filename = :filename\n"
846 ", tooltip = :tooltip\n"
847 ", thumbnail = :thumbnail\n"
849 ", md5sum = :md5sum\n"
852 qWarning() <<
"Could not prepare updateResource statement" << q.lastError();
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());
862 buf.open(QBuffer::WriteOnly);
863 resource->thumbnail().save(&buf,
"PNG");
865 q.bindValue(
":thumbnail", buf.data());
866 q.bindValue(
":id", resourceId);
870 qWarning() <<
"Could not update resource" << q.boundValues() << q.lastError();
873 if (!resource->metadata().isEmpty()) {
886 r = q.prepare(
"DELETE FROM versioned_resources \n"
887 "WHERE resource_id = :resource_id;");
890 qWarning() <<
"Could not prepare removeResourceCompletely1 statement" << q.lastError();
894 q.bindValue(
":resource_id", resourceId);
897 qWarning() <<
"Could not execute removeResourceCompletely1 statement" << q.lastError() << resourceId;
904 r = q.prepare(
"DELETE FROM resources \n"
905 "WHERE id = :resource_id;");
908 qWarning() <<
"Could not prepare removeResourceCompletely2 statement" << q.lastError();
912 q.bindValue(
":resource_id", resourceId);
915 qWarning() <<
"Could not execute removeResourceCompletely2 statement" << q.lastError() << resourceId;
922 r = q.prepare(
"DELETE FROM resource_tags \n"
923 "WHERE resource_id = :resource_id;");
926 qWarning() <<
"Could not prepare removeResourceCompletely3 statement" << q.lastError();
930 q.bindValue(
":resource_id", resourceId);
933 qWarning() <<
"Could not execute removeResourceCompletely3 statement" << q.lastError() << resourceId;
940 r = q.prepare(
"DELETE FROM metadata \n"
941 "WHERE foreign_id = :resource_id\n"
942 "AND table_name = :table;");
945 qWarning() <<
"Could not prepare removeResourceCompletely4 statement" << q.lastError();
949 q.bindValue(
":resource_id", resourceId);
953 qWarning() <<
"Could not execute removeResourceCompletely4 statement" << q.lastError() << resourceId;
965 bool r = q.prepare(
"SELECT resources.id FROM resources\n"
968 "WHERE resources.filename = :filename\n"
969 "AND resource_types.id = resources.resource_type_id\n"
970 "AND resource_types.name = :resourceType\n"
971 "AND resources.storage_id = storages.id\n"
972 "AND storages.location = :storageLocation");
975 qWarning() <<
"Could not prepare getResourceIdFromFilename statement" << q.lastError() << q.executedQuery();
979 q.bindValue(
":filename", filename);
980 q.bindValue(
":resourceType", resourceType);
985 qWarning() <<
"Could not execute getResourceIdFromFilename statement" << q.lastError() << filename << resourceType;
991 outResourceId = q.value(
"resources.id").toInt();
1001 bool r = q.prepare(
"SELECT resource_id FROM versioned_resources\n"
1003 ", resource_types\n"
1005 "WHERE versioned_resources.filename = :filename\n"
1006 "AND resources.id = versioned_resources.resource_id\n"
1007 "AND resource_types.id = resources.resource_type_id\n"
1008 "AND resource_types.name = :resourceType\n"
1009 "AND resources.storage_id = storages.id\n"
1010 "AND storages.location = :storageLocation");
1013 qWarning() <<
"Could not prepare getResourceIdFromVersionedFilename statement" << q.lastError() << q.executedQuery();
1018 q.bindValue(
":filename", filename);
1019 q.bindValue(
":resourceType", resourceType);
1024 qWarning() <<
"Could not execute getResourceIdFromVersionedFilename statement" << q.lastError() << filename << resourceType;
1030 outResourceId = q.value(
"resource_id").toInt();
1039 bool r = q.prepare(
"SELECT filename FROM versioned_resources \n"
1040 "WHERE resource_id = :resource_id;");
1043 qWarning() <<
"Could not prepare getAllVersionsLocations statement" << q.lastError();
1047 q.bindValue(
":resource_id", resourceId);
1050 qWarning() <<
"Could not execute getAllVersionsLocations statement" << q.lastError() << resourceId;
1056 outVersionsLocationsList << q.value(
"filename").toString();
1068 qWarning() <<
"KisResourceCacheDb::addResource: The database is not valid";
1072 if (!resource || !resource->valid()) {
1073 qWarning() <<
"KisResourceCacheDb::addResource: The resource is not valid:" << resource->filename();
1081 if (resourceId > -1) {
1086 r = q.prepare(
"INSERT INTO resources \n"
1087 "(storage_id, resource_type_id, name, filename, tooltip, thumbnail, status, temporary, md5sum) \n"
1091 " WHERE location = :storage_location)\n"
1093 " FROM resource_types\n"
1094 " WHERE name = :resource_type)\n"
1104 qWarning() <<
"Could not prepare addResource statement" << q.lastError();
1108 q.bindValue(
":resource_type", resourceType);
1110 q.bindValue(
":name", resource->name());
1111 q.bindValue(
":filename", resource->filename());
1113 QString translationContext;
1116 +
":" + resourceType +
"/" + resource->filename();
1117 }
else if (storage->location() ==
"memory") {
1118 translationContext =
"memory/" + resourceType +
"/" + resource->filename();
1120 else if (resource->filename().endsWith(
".myb", Qt::CaseInsensitive)) {
1121 translationContext =
"./plugins/paintops/mypaint/brushes/" + resource->filename();
1123 translationContext =
"./krita/data/" + resourceType +
"/" + resource->filename();
1127 QByteArray ctx = translationContext.toUtf8();
1128 QString translatedName = i18nc(ctx, resource->name().toUtf8());
1129 if (translatedName == resource->name()) {
1131 QString altName = QFileInfo(resource->filename()).completeBaseName().replace(
'_',
' ');
1132 QString altTranslatedName = i18nc(ctx, altName.toUtf8());
1133 if (altName != altTranslatedName) {
1134 translatedName = altTranslatedName;
1137 q.bindValue(
":tooltip", translatedName);
1141 buf.open(QBuffer::WriteOnly);
1142 resource->image().save(&buf,
"PNG");
1144 q.bindValue(
":thumbnail", buf.data());
1146 q.bindValue(
":status", resource->active());
1147 q.bindValue(
":temporary", (temporary ? 1 : 0));
1148 q.bindValue(
":md5sum", resource->md5Sum());
1152 qWarning() <<
"Could not execute addResource statement" << q.lastError() << q.boundValues();
1157 if (resourceId < 0) {
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: "
1161 << resource->filename()
1167 resource->setResourceId(resourceId);
1170 qWarning() <<
"Could not add resource version" << resource;
1174 if (!resource->metadata().isEmpty()) {
1185 QSqlDatabase::database().transaction();
1187 while (iter->hasNext()) {
1193 int resourceId = -1;
1195 while (verIt->hasNext()) {
1199 if (resource && resource->valid()) {
1200 resource->setVersion(verIt->guessedVersion());
1201 resource->setMD5Sum(storage->resourceMd5(verIt->url()));
1203 if (resourceId < 0) {
1204 if (
addResource(storage, iter->lastModified(), resource, iter->type())) {
1205 resourceId = resource->resourceId();
1207 qWarning() <<
"Could not add resource" << resource->filename() <<
"to the database";
1211 qWarning() <<
"Could not add resource version" << resource->filename() <<
"to the database";
1217 QSqlDatabase::database().commit();
1223 if (resourceId < 0) {
1224 qWarning() <<
"Invalid resource id; cannot remove resource";
1228 bool r = q.prepare(
"UPDATE resources\n"
1229 "SET status = :status\n"
1230 "WHERE id = :resource_id");
1232 qWarning() <<
"Could not prepare removeResource query" << q.lastError();
1234 q.bindValue(
":status", active);
1235 q.bindValue(
":resource_id", resourceId);
1237 qWarning() <<
"Could not update resource" << resourceId <<
"to inactive" << q.lastError();
1249 QFile f(
":/select_tag.sql");
1250 if (f.open(QFile::ReadOnly)) {
1252 if (!q.prepare(f.readAll())) {
1253 qWarning() <<
"Could not read and prepare select_tag.sql" << q.lastError();
1256 q.bindValue(
":url", tag->url());
1257 q.bindValue(
":resource_type", resourceType);
1260 qWarning() <<
"Could not query tags" << q.boundValues() << q.lastError();
1265 qWarning() <<
"Could not find tag" << q.boundValues() << q.lastError();
1269 tagId = q.value(0).toInt();
1276 bool r = q.prepare(
"SELECT resources.id\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");
1283 qWarning() <<
"Could not prepare tagResource query" << q.lastError();
1287 q.bindValue(
":resource_type", resourceType);
1288 q.bindValue(
":resource_filename", resourceFileName);
1291 qWarning() <<
"Could not execute tagResource statement" << q.boundValues() << q.lastError();
1298 int resourceId = q.value(0).toInt();
1300 if (resourceId < 0) {
1301 qWarning() <<
"Could not find resource to tag" << resourceFileName << resourceType;
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();
1314 q.bindValue(
":resource_id", resourceId);
1315 q.bindValue(
":tag_id", tagId);
1318 qWarning() <<
"Could not execute tagResource query 2" << q.lastError() << q.boundValues();
1323 int count = q.value(0).toInt();
1331 if (!q.prepare(
"INSERT INTO resource_tags\n"
1332 "(resource_id, tag_id)\n"
1334 "(:resource_id, :tag_id);")) {
1335 qWarning() <<
"Could not prepare tagResource insert statement" << q.lastError();
1339 q.bindValue(
":resource_id", resourceId);
1340 q.bindValue(
":tag_id", tagId);
1343 qWarning() <<
"Could not execute tagResource stagement" << q.boundValues() << q.lastError();
1353 QFile f(
":/select_tag.sql");
1354 if (f.open(QFile::ReadOnly)) {
1356 if (!q.prepare(f.readAll())) {
1357 qWarning() <<
"Could not read and prepare select_tag.sql" << q.lastError();
1360 q.bindValue(
":url", url);
1361 q.bindValue(
":resource_type", resourceType);
1363 qWarning() <<
"Could not query tags" << q.boundValues() << q.lastError();
1367 qWarning() <<
"Could not open select_tag.sql";
1374 if (!q.prepare(
"INSERT INTO tags_storages\n"
1375 "(tag_id, storage_id)\n"
1380 " WHERE url = :url\n"
1381 " AND resource_type_id = (SELECT id \n"
1382 " FROM resource_types\n"
1383 " WHERE name = :resource_type)"
1387 " WHERE location = :storage_location\n"
1390 qWarning() <<
"Could not prepare add tag/storage statement" << q.lastError();
1394 q.bindValue(
":url", url);
1395 q.bindValue(
":resource_type", resourceType);
1399 qWarning() <<
"Could not insert tag/storage link" << q.boundValues() << q.lastError();
1409 if (
hasTag(tag->url(), resourceType)) {
1412 if (!q.prepare(
"SELECT storages.location\n"
1413 "FROM tags_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"))
1423 qWarning() <<
"Could not prepare select tags from tags_storages query" << q.lastError();
1426 q.bindValue(
":url", tag->url());
1427 q.bindValue(
":resource_type", resourceType);
1430 qWarning() <<
"Could not execute tags_storages query" << q.boundValues() << q.lastError();
1446 if (!q.prepare(
"INSERT INTO tags\n"
1447 "(url, name, comment, resource_type_id, active, filename)\n"
1453 " FROM resource_types\n"
1454 " WHERE name = :resource_type)\n"
1458 qWarning() <<
"Could not prepare insert tag statement" << q.lastError();
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());
1469 qWarning() <<
"Could not insert tag" << q.boundValues() << q.lastError();
1472 tagId = q.lastInsertId().toInt();
1476 Q_FOREACH(
const QString language, tag->names().keys()) {
1478 QString name = tag->names()[language];
1479 QString comment = name;
1480 if (tag->comments().contains(language)) {
1481 comment = tag->comments()[language];
1485 if (!q.prepare(
"INSERT INTO tag_translations\n"
1497 qWarning() <<
"Could not prepare insert tag_translation query" << q.lastError();
1500 q.bindValue(
":id", tagId);
1501 q.bindValue(
":language", language);
1502 q.bindValue(
":name", name);
1503 q.bindValue(
":comment", comment);
1506 qWarning() <<
"Could not execute insert tag_translation query" << q.lastError() << q.boundValues();
1520 QSqlDatabase::database().transaction();
1522 while(iter->hasNext()) {
1525 if (tag && tag->valid()) {
1526 if (!
addTag(resourceType, storage->location(), tag)) {
1527 qWarning() <<
"Could not add tag" << tag <<
"to the database";
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();
1539 QSqlDatabase::database().commit();
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();
1556 q.bindValue(
":storage_type", name);
1558 qWarning() <<
"Could not execute select from storage_types query" << q.lastError();
1562 int rowCount = q.value(0).toInt();
1568 QFile f(
":/fill_storage_types.sql");
1569 if (f.open(QFile::ReadOnly)) {
1570 QString sql = f.readAll();
1572 q.addBindValue(name);
1574 qWarning() <<
"Could not insert" << name << q.lastError();
1579 qWarning() <<
"Could not open fill_storage_types.sql";
1588 qWarning() <<
"The database is not valid";
1594 r = q.prepare(
"SELECT * FROM storages WHERE location = :location");
1598 qWarning() <<
"Could not select from storages";
1611 r = q.prepare(
"INSERT INTO storages\n "
1612 "(storage_type_id, location, timestamp, pre_installed, active, thumbnail)\n"
1614 "(:storage_type_id, :location, :timestamp, :pre_installed, :active, :thumbnail);");
1617 qWarning() <<
"Could not prepare query" << q.lastError();
1621 const QString sanitizedStorageLocation =
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);
1631 buf.open(QBuffer::WriteOnly);
1632 storage->thumbnail().save(&buf,
"PNG");
1634 q.bindValue(
":thumbnail", buf.data());
1638 if (!r) qWarning() <<
"Could not execute query" << q.lastError();
1640 if (!q.prepare(
"SELECT id\n"
1642 "WHERE location = :location\n")) {
1643 qWarning() <<
"Could not prepare storage id statement" << q.lastError();
1646 q.bindValue(
":location", sanitizedStorageLocation);
1648 qWarning() <<
"Could not execute storage id statement" << q.boundValues() << q.lastError();
1652 qWarning() <<
"Could not find id for the newly added storage" << q.lastError();
1654 storage->setStorageId(q.value(
"id").toInt());
1661 if (keys.size() > 0 && storage->storageId() >= 0) {
1663 QMap<QString, QVariant> metadata;
1665 Q_FOREACH(
const QString &key, storage->metaDataKeys()) {
1666 metadata[key] = storage->metaData(key);
1675 qWarning() <<
"Failed to add all resources for storage" << storage;
1689 qWarning() <<
"Failed to add all tags for storage" << storage;
1704 KisSqlQueryLoader loader(
":/sql/delete_versioned_resources_for_storage_indirect.sql",
1711 KisSqlQueryLoader loader(
":/sql/delete_versioned_resources_for_storage_direct.sql",
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();
1732 "DELETE FROM resources\n"
1733 "WHERE storage_id = (SELECT storages.id\n"
1735 " WHERE storages.location = :location)\n",
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"
1749 " WHERE storages.location = :location)\n"
1758 "DELETE FROM tags_storages \n"
1759 "WHERE tags_storages.storage_id = \n"
1760 " (SELECT storages.id\n"
1762 " WHERE storages.location = :location)",
1770 "DELETE FROM metadata\n"
1771 "WHERE foreign_id = (SELECT storages.id\n"
1773 " WHERE storages.location = :location)"
1774 "AND table_name = :table;",
1783 "DELETE FROM storages\n"
1784 "WHERE location = :location;",
1790 transactionLock.
commit();
1793 qWarning().noquote() <<
"ERROR: deleteStorage:" << e.
message;
1794 qWarning().noquote() <<
" file:" << e.
filePath;
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();
1814struct ResourceVersion :
public boost::less_than_comparable<ResourceVersion>
1816 int resourceId = -1;
1818 QDateTime timestamp;
1821 bool operator<(
const ResourceVersion &rhs)
const {
1822 return resourceId < rhs.resourceId ||
1823 (resourceId == rhs.resourceId && version < rhs.version);
1826 struct CompareByResourceId {
1827 bool operator() (
const ResourceVersion &lhs,
const ResourceVersion &rhs)
const {
1828 return lhs.resourceId < rhs.resourceId;
1836QDebug
operator<<(QDebug dbg,
const ResourceVersion &ver)
1838 dbg.nospace() <<
"ResourceVersion("
1839 << ver.resourceId <<
", "
1840 << ver.version <<
", "
1841 << ver.timestamp <<
", "
1854 qWarning() <<
"KisResourceCacheDb::addResource: The database is not valid";
1858 bool success =
true;
1862 if (!q.prepare(
"SELECT id\n"
1866 "WHERE location = :location\n")) {
1867 qWarning() <<
"Could not prepare storage timestamp statement" << q.lastError();
1872 qWarning() <<
"Could not execute storage timestamp statement" << q.boundValues() << q.lastError();
1877 debugResource <<
"Adding storage to the database:" << storage;
1879 qWarning() <<
"Could not add new storage" << storage->name() <<
"to the database";
1885 storage->setStorageId(q.value(
"id").toInt());
1888 QSqlDatabase::database().transaction();
1904 int nextInexistentResourceId = std::numeric_limits<int>::min();
1907 while (iter->hasNext()) {
1910 const int firstResourceVersionPosition = resourcesInStorage.size();
1912 int detectedResourceId = nextInexistentResourceId;
1916 while (verIt->hasNext()) {
1923 QString path = QDir::fromNativeSeparators(verIt->url());
1924 int folderEndIdx = path.indexOf(
"/");
1925 QString properFilenameWithSubfolders = path.right(path.length() - folderEndIdx - 1);
1930 ResourceVersion item;
1931 item.url = verIt->url();
1932 item.version = verIt->guessedVersion();
1935 item.timestamp = QDateTime::fromSecsSinceEpoch(verIt->lastModified().toSecsSinceEpoch());
1937 item.resourceId = id;
1939 if (detectedResourceId < 0 && id >= 0) {
1940 detectedResourceId = id;
1943 resourcesInStorage.append(item);
1950 for (
int i = firstResourceVersionPosition; i < resourcesInStorage.size(); i++) {
1951 if (resourcesInStorage[i].resourceId < 0) {
1952 resourcesInStorage[i].resourceId = detectedResourceId;
1956 nextInexistentResourceId++;
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"
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();
1979 q.bindValue(
":resource_type", resourceType);
1980 q.bindValue(
":storage_id",
int(storage->storageId()));
1983 qWarning() <<
"Could not exec resource by type query" << q.boundValues() << q.lastError();
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();
1995 resourcesInDatabase.append(item);
1998 QSet<int> resourceIdForUpdate;
2000 std::sort(resourcesInStorage.begin(), resourcesInStorage.end());
2001 std::sort(resourcesInDatabase.begin(), resourcesInDatabase.end());
2003 auto itA = resourcesInStorage.begin();
2004 auto endA = resourcesInStorage.end();
2006 auto itB = resourcesInDatabase.begin();
2007 auto endB = resourcesInDatabase.end();
2013 while (itA != endA) {
2014 if (itA->resourceId >= 0)
break;
2024 res->setVersion(itA->version);
2025 res->setMD5Sum(storage->resourceMd5(itA->url));
2026 if (!res->valid()) {
2032 const bool retval =
addResource(storage, itA->timestamp, res, resourceType);
2039 const int resourceId = res->resourceId();
2046 auto nextResource = std::upper_bound(itA, endA, *itA, ResourceVersion::CompareByResourceId());
2047 for (
auto it = std::next(itA); it != nextResource; ++it) {
2049 res->setVersion(it->version);
2050 res->setMD5Sum(storage->resourceMd5(it->url));
2051 if (!res->valid()) {
2073 while (itA != endA || itB != endB) {
2074 if ((itA != endA && itB != endB && *itA < *itB) ||
2081 res->setVersion(itA->version);
2082 res->setMD5Sum(storage->resourceMd5(itA->url));
2087 resourceIdForUpdate.insert(itA->resourceId);
2091 }
else if ((itA != endA && itB != endB && *itA > *itB) ||
2097 resourceIdForUpdate.insert(itB->resourceId);
2112 for (
auto it = resourceIdForUpdate.begin(); it != resourceIdForUpdate.end(); ++it) {
2117 QSqlDatabase::database().commit();
2118 debugResource <<
"Synchronizing the storages took" << t.elapsed() <<
"milliseconds for" << storage->location();
2133 "inline://delete_metadata_for_resources_in_memory_storages",
2134 "DELETE FROM metadata\n"
2135 "WHERE foreign_id IN (SELECT id\n"
2137 " WHERE storage_id in (SELECT id\n"
2139 " WHERE storage_type_id == :storage_type))\n"
2140 "AND table_name = :table",
2149 "DELETE FROM metadata\n"
2150 "WHERE foreign_id IN (SELECT id\n"
2152 " WHERE temporary = 1)\n"
2153 "AND table_name = :table",
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"
2164 " WHERE storage_type_id == :storage_type)",
2172 "DELETE FROM versioned_resources\n"
2173 "WHERE resource_id IN (SELECT id FROM resources\n"
2174 " WHERE temporary = 1)",
2180 KisSqlQueryLoader loader(
"inline://delete_current_resources_in_temporary_storages",
2181 "DELETE FROM resources\n"
2182 "WHERE storage_id in (SELECT id\n"
2184 " WHERE storage_type_id == :storage_type)",
2192 "DELETE FROM resources\n"
2193 "WHERE temporary = 1",
2204 "DELETE FROM metadata\n"
2205 "WHERE foreign_id IN (SELECT id\n"
2207 " WHERE storage_type_id == :storage_type)\n"
2208 "AND table_name = :table;",
2217 "DELETE FROM storages\n"
2218 "WHERE storage_type_id == :storage_type\n",
2224 transactionLock.
commit();
2226 qWarning().noquote() <<
"ERROR: failed to execute query:" << e.
message;
2227 qWarning().noquote() <<
" file:" << e.
filePath;
2229 qWarning().noquote() <<
" error:" << e.
sqlError.text();
2237 if (!q.prepare(
"PRAGMA optimize;")) {
2238 qWarning() <<
"Could not prepare query" << q.lastQuery() << q.lastError();
2242 qWarning() <<
"Could not execute query" << q.lastQuery() << q.lastError();
2249 QString(
"PRAGMA foreign_keys = %1").arg(isEnabled ?
"ON" :
"OFF"));
2256 "PRAGMA foreign_keys");
2260 if (loader.
query().first()) {
2261 return loader.
query().value(0).toInt();
2270 bool useForeignKeys =
false;
2271 KisUsageLogger::log(
"INFO: detected stable build of Krita, foreign_keys constraint will be disabled");
2273 bool useForeignKeys =
true;
2274 KisUsageLogger::log(
"INFO: detected unstable build of Krita, foreign_keys constraint will be enabled");
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));
2285 if (oldForeignKeysState != useForeignKeys) {
2287 "INFO: switch foreign_keys state: " +
2288 QString::number(oldForeignKeysState) +
2290 QString::number(useForeignKeys));
2296 qWarning().noquote() <<
"ERROR: failed to execute query:" << e.
message;
2297 qWarning().noquote() <<
" file:" << e.
filePath;
2299 qWarning().noquote() <<
" error:" << e.
sqlError.text();
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();
2315 q.bindValue(
":resource_type", resourceType);
2317 qWarning() <<
"Could not execute select from resource_types query" << q.lastError();
2321 int rowCount = q.value(0).toInt();
2327 QFile f(
":/fill_resource_types.sql");
2328 if (f.open(QFile::ReadOnly)) {
2329 QString sql = f.readAll();
2331 q.addBindValue(resourceType);
2333 qWarning() <<
"Could not insert" << resourceType << q.lastError();
2338 qWarning() <<
"Could not open fill_resource_types.sql";
2344 QMap<QString, QVariant> map;
2347 q.setForwardOnly(
true);
2348 if (!q.prepare(
"SELECT key\n"
2351 "WHERE foreign_id = :id\n"
2352 "AND table_name = :table")) {
2353 qWarning() <<
"Could not prepare metadata query" << q.lastError();
2357 q.bindValue(
":id",
id);
2358 q.bindValue(
":table", tableName);
2361 qWarning() <<
"Could not execute metadata query" << q.lastError();
2366 QString key = q.value(0).toString();
2367 QByteArray ba = q.value(1).toByteArray();
2368 if (!ba.isEmpty()) {
2369 QDataStream ds(QByteArray::fromBase64(ba));
2371 ds.setVersion(QDataStream::Qt_5_15);
2382 QSqlDatabase::database().transaction();
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();
2394 q.bindValue(
":id",
id);
2395 q.bindValue(
":table", tableName);
2398 QSqlDatabase::database().rollback();
2399 qWarning() <<
"Could not execute delete metadata query" << q.lastError();
2406 QSqlDatabase::database().commit();
2409 QSqlDatabase::database().rollback();
2418 if (!q.prepare(
"INSERT INTO metadata\n"
2419 "(foreign_id, table_name, key, value)\n"
2421 "(:id, :table, :key, :value)")) {
2422 QSqlDatabase::database().rollback();
2423 qWarning() <<
"Could not create insert metadata query" << q.lastError();
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());
2433 QVariant
v = iter.value();
2434 if (!
v.isNull() &&
v.isValid()) {
2436 QDataStream ds(&ba, QIODevice::WriteOnly);
2437 ds.setVersion(QDataStream::Qt_5_15);
2440 q.bindValue(
":value", QString::fromLatin1(ba));
2443 qWarning() <<
"Could not insert metadata" << q.lastError();
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")
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();
2474 transactionLock.
commit();
2477 qWarning().noquote() <<
"ERROR: failed to execute query:" << e.
message;
2478 qWarning().noquote() <<
" file:" << e.
filePath;
2480 qWarning().noquote() <<
" error:" << e.
sqlError.text();
float value(const T *src, size_t ch)
QDebug KRITACOMMAND_EXPORT operator<<(QDebug dbg, const KisCumulativeUndoData &data)
QList< QString > QStringList
QString changeToEmptyIfNull(QString s)
bool updateSchemaVersion()
QSqlError runUpdateScriptFile(const QString &path, const QString &message)
QSqlError runUpdateScript(const QString &script, const QString &message)
const QString METADATA_RESOURCES
QSqlError createDatabase(const QString &location)
const QString METADATA_STORAGES
bool operator<(KoSnapStrategy::SnapType lhs, KoSnapStrategy::SnapType rhs)
static bool numberedBackupFile(const QString &filename, const QString &backupDir=QString(), const QString &backupExtension=QStringLiteral("~"), const uint maxBackups=10)
static QStringList disabledBundles
the list of compatibility bundles that need to inactive by default
static bool addStorage(KisResourceStorageSP storage, bool preinstalled)
static bool addResource(KisResourceStorageSP storage, QDateTime timestamp, KoResourceSP resource, const QString &resourceType)
static void setForeignKeysStateImpl(bool isEnabled)
static bool isValid()
isValid
static bool resourceNeedsUpdating(int resourceId, QDateTime timestamp)
static bool getAllVersionsLocations(int resourceId, QStringList &outVersionsLocationsList)
static bool addResourceVersionImpl(int resourceId, QDateTime timestamp, KisResourceStorageSP storage, KoResourceSP resource)
static bool getForeignKeysStateImpl()
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...
static void performHouseKeepingOnExit()
perform optimize and vacuum when necessary
static bool removeOrphanedMetaData()
removeOrphanedMetaData Previous versions of Krita never removed metadata, so this function doublechec...
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...
static void deleteTemporaryResources()
Delete all storages that are Unknown or Memory and all resources that are marked temporary or belong ...
static QMap< QString, QVariant > metaDataForId(int id, const QString &tableName)
metaDataForId
static bool initialize(const QString &location)
initializes the database and updates the scheme if necessary. Does not actually fill the database wit...
static bool removeResourceVersionImpl(int resourceId, int version, KisResourceStorageSP storage)
static bool addTag(const QString &resourceType, const QString storageLocation, KisTagSP tag)
static QString lastError()
lastError returns the last SQL error.
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 const QString resourceCacheDbFilename
filename of the database
static bool linkTagToStorage(const QString &url, const QString &resourceType, const QString &storageLocation)
static bool deleteStorage(KisResourceStorageSP storage)
Actually delete the storage and all its resources from the database (i.e., nothing is set to inactive...
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 addMetaDataForId(const QMap< QString, QVariant > map, int id, const QString &tableName)
static QStringList storageTypes
kinds of places where resources can be stored
static bool addResources(KisResourceStorageSP storage, QString resourceType)
static bool removeResourceCompletely(int resourceId)
static bool addStorageTags(KisResourceStorageSP storage)
static QString s_lastError
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...
static const QString databaseVersion
current schema version
static void synchronizeForeignKeysState()
static bool registerResourceType(const QString &resourceType)
registerResourceType registers this resource type in the database
static bool makeResourceTheCurrentVersion(int resourceId, KoResourceSP resource)
static bool synchronizeStorage(KisResourceStorageSP storage)
static bool registerStorageType(const KisResourceStorage::StorageType storageType)
registerStorageType registers this storage type in the database
static bool updateResourceTableForResourceIfNeeded(int resourceId, const QString &resourceType, KisResourceStorageSP storage)
static bool hasTag(const QString &url, const QString &resourceType)
static bool addTags(KisResourceStorageSP storage, QString resourceType)
static bool tagResource(const QString &resourceFileName, KisTagSP tag, const QString &resourceType)
static KisResourceLoaderRegistry * instance()
QString makeStorageLocationRelative(QString location) const
static void saveTags()
saveTags saves all tags to .tag files in the resource folder
static KisResourceLocator * instance()
static QString storageTypeToUntranslatedString(StorageType storageType)
static constexpr single_statement_mode_t single_statement_mode
static void log(const QString &message)
Logs with date/time.
#define KIS_SAFE_ASSERT_RECOVER(cond)
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
KRITAVERSION_EXPORT QString versionString(bool checkGit=false)