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());
513 loader.
query().bindValue(
":resource_type", resourceType);
520 while (loader.
query().next()) {
521 if (loader.
query().value(
"ref_count").toInt() > 1) {
522 sharedTags << loader.
query().value(
"tag_id").toInt();
524 uniqueTags << loader.
query().value(
"tag_id").toInt();
528 return {uniqueTags, sharedTags};
531 qWarning().noquote() <<
"ERROR: deleteStorage:" << e.
message;
532 qWarning().noquote() <<
" file:" << e.
filePath;
536 qWarning().noquote() <<
"ERROR: deleteStorage:" << e.
message;
537 qWarning().noquote() <<
" file:" << e.
filePath;
538 qWarning().noquote() <<
" statement:" << e.statementIndex;
539 qWarning().noquote() <<
" error:" << e.sqlError.text();
552 if (!q.prepare(
"SELECT resources.id\n"
556 "WHERE resources.resource_type_id = resource_types.id\n"
557 "AND storages.id = resources.storage_id\n"
558 "AND storages.location = :storage_location\n"
559 "AND resource_types.name = :resource_type\n")) {
561 qWarning() <<
"Could not read and prepare resourcesForStorage" << q.lastError();
565 q.bindValue(
":resource_type", resourceType);
569 qWarning() <<
"Could not query resourceIdForResource" << q.boundValues() << q.lastError();
574 result << q.value(0).toInt();
586 if (!q.prepare(
"SELECT resources.id\n"
590 "WHERE resources.resource_type_id = resource_types.id\n"
591 "AND storages.id = resources.storage_id\n"
592 "AND storages.location = :storage_location\n"
593 "AND resource_types.name = :resource_type\n"
594 "AND resources.filename = :filename\n")) {
595 qWarning() <<
"Could not read and prepare resourceIdForResource" << q.lastError();
599 q.bindValue(
":filename", resourceFileName);
600 q.bindValue(
":resource_type", resourceType);
604 qWarning() <<
"Could not query resourceIdForResource" << q.boundValues() << q.lastError();
609 return q.value(0).toInt();
614 if (!q.prepare(
"SELECT versioned_resources.resource_id\n"
617 ", versioned_resources\n"
619 "WHERE resources.resource_type_id = resource_types.id\n"
620 "AND versioned_resources.resource_id = resources.id\n"
621 "AND storages.id = versioned_resources.storage_id\n"
622 "AND storages.location = :storage_location\n"
623 "AND resource_types.name = :resource_type\n"
624 "AND versioned_resources.filename = :filename\n")) {
625 qWarning() <<
"Could not read and prepare resourceIdForResource (in versioned resources)" << q.lastError();
629 q.bindValue(
":filename", resourceFileName);
630 q.bindValue(
":resource_type", resourceType);
634 qWarning() <<
"Could not query resourceIdForResource (in versioned resources)" << q.boundValues() << q.lastError();
639 return q.value(0).toInt();
651 if (!q.prepare(
"SELECT timestamp\n"
652 "FROM versioned_resources\n"
653 "WHERE resource_id = :resource_id\n"
654 "AND version = (SELECT MAX(version)\n"
655 " FROM versioned_resources\n"
656 " WHERE resource_id = :resource_id);")) {
657 qWarning() <<
"Could not prepare resourceNeedsUpdating statement" << q.lastError();
661 q.bindValue(
":resource_id", resourceId);
664 qWarning() <<
"Could not query for the most recent timestamp" << q.boundValues() << q.lastError();
669 qWarning() <<
"Inconsistent database: could not find a version for resource with Id" << resourceId;
673 QVariant resourceTimeStamp = q.value(0);
675 if (!resourceTimeStamp.isValid()) {
676 qWarning() <<
"Could not retrieve timestamp from versioned_resources" << resourceId;
680 return (timestamp.toSecsSinceEpoch() > resourceTimeStamp.toInt());
708 Q_ASSERT(resource->version() >= 0);
711 r = q.prepare(
"INSERT INTO versioned_resources \n"
712 "(resource_id, storage_id, version, filename, timestamp, md5sum)\n"
717 " WHERE location = :storage_location)\n"
725 qWarning() <<
"Could not prepare addResourceVersion statement" << q.lastError();
729 q.bindValue(
":resource_id", resourceId);
731 q.bindValue(
":version", resource->version());
732 q.bindValue(
":filename", resource->filename());
733 q.bindValue(
":timestamp", timestamp.toSecsSinceEpoch());
735 q.bindValue(
":md5sum", resource->md5Sum());
739 qWarning() <<
"Could not execute addResourceVersionImpl statement" << q.lastError() << resourceId << storage->name() << storage->location() << resource->name() << resource->filename() <<
"version" << resource->version();
755 r = q.prepare(
"DELETE FROM versioned_resources \n"
756 "WHERE resource_id = :resource_id\n"
757 "AND version = :version\n"
758 "AND storage_id = (SELECT id \n"
760 " WHERE location = :storage_location);");
763 qWarning() <<
"Could not prepare removeResourceVersionImpl statement" << q.lastError();
767 q.bindValue(
":resource_id", resourceId);
769 q.bindValue(
":version", version);
773 qWarning() <<
"Could not execute removeResourceVersionImpl statement" << q.lastError() << resourceId << storage->name() << storage->location() <<
"version" << version;
787 r = q.prepare(
"SELECT MAX(version)\n"
788 "FROM versioned_resources\n"
789 "WHERE resource_id = :resource_id;");
791 qWarning() <<
"Could not prepare findMaxVersion statement" << q.lastError();
795 q.bindValue(
":resource_id", resourceId);
799 qWarning() <<
"Could not execute findMaxVersion query" << q.boundValues() << q.lastError();
806 maxVersion = q.value(0).toInt();
809 QString maxVersionFilename;
812 r = q.prepare(
"SELECT filename\n"
813 "FROM versioned_resources\n"
814 "WHERE resource_id = :resource_id\n"
815 "AND version = :version;");
817 qWarning() <<
"Could not prepare findMaxVersionFilename statement" << q.lastError();
821 q.bindValue(
":resource_id", resourceId);
822 q.bindValue(
":version", maxVersion);
826 qWarning() <<
"Could not execute findMaxVersionFilename query" << q.boundValues() << q.lastError();
833 maxVersionFilename = q.value(0).toString();
837 QString currentFilename;
840 r = q.prepare(
"SELECT filename\n"
842 "WHERE id = :resource_id;");
844 qWarning() <<
"Could not prepare findMaxVersion statement" << q.lastError();
848 q.bindValue(
":resource_id", resourceId);
852 qWarning() <<
"Could not execute findMaxVersion query" << q.boundValues() << q.lastError();
859 currentFilename = q.value(0).toString();
862 if (currentFilename != maxVersionFilename) {
863 const QString url = resourceType +
"/" + maxVersionFilename;
866 resource->setVersion(maxVersion);
867 resource->setMD5Sum(storage->resourceMd5(url));
868 resource->setStorageLocation(storage->location());
880 r = q.prepare(
"UPDATE resources\n"
882 ", filename = :filename\n"
883 ", tooltip = :tooltip\n"
884 ", thumbnail = :thumbnail\n"
886 ", md5sum = :md5sum\n"
889 qWarning() <<
"Could not prepare updateResource statement" << q.lastError();
893 q.bindValue(
":name", resource->name());
894 q.bindValue(
":filename", resource->filename());
895 q.bindValue(
":tooltip", i18n(resource->name().toUtf8()));
896 q.bindValue(
":md5sum", resource->md5Sum());
899 buf.open(QBuffer::WriteOnly);
900 resource->thumbnail().save(&buf,
"PNG");
902 q.bindValue(
":thumbnail", buf.data());
903 q.bindValue(
":id", resourceId);
907 qWarning() <<
"Could not update resource" << q.boundValues() << q.lastError();
910 if (!resource->metadata().isEmpty()) {
923 r = q.prepare(
"DELETE FROM versioned_resources \n"
924 "WHERE resource_id = :resource_id;");
927 qWarning() <<
"Could not prepare removeResourceCompletely1 statement" << q.lastError();
931 q.bindValue(
":resource_id", resourceId);
934 qWarning() <<
"Could not execute removeResourceCompletely1 statement" << q.lastError() << resourceId;
941 r = q.prepare(
"DELETE FROM resources \n"
942 "WHERE id = :resource_id;");
945 qWarning() <<
"Could not prepare removeResourceCompletely2 statement" << q.lastError();
949 q.bindValue(
":resource_id", resourceId);
952 qWarning() <<
"Could not execute removeResourceCompletely2 statement" << q.lastError() << resourceId;
959 r = q.prepare(
"DELETE FROM resource_tags \n"
960 "WHERE resource_id = :resource_id;");
963 qWarning() <<
"Could not prepare removeResourceCompletely3 statement" << q.lastError();
967 q.bindValue(
":resource_id", resourceId);
970 qWarning() <<
"Could not execute removeResourceCompletely3 statement" << q.lastError() << resourceId;
977 r = q.prepare(
"DELETE FROM metadata \n"
978 "WHERE foreign_id = :resource_id\n"
979 "AND table_name = :table;");
982 qWarning() <<
"Could not prepare removeResourceCompletely4 statement" << q.lastError();
986 q.bindValue(
":resource_id", resourceId);
990 qWarning() <<
"Could not execute removeResourceCompletely4 statement" << q.lastError() << resourceId;
1002 bool r = q.prepare(
"SELECT resources.id FROM resources\n"
1003 ", resource_types\n"
1005 "WHERE resources.filename = :filename\n"
1006 "AND resource_types.id = resources.resource_type_id\n"
1007 "AND resource_types.name = :resourceType\n"
1008 "AND resources.storage_id = storages.id\n"
1009 "AND storages.location = :storageLocation");
1012 qWarning() <<
"Could not prepare getResourceIdFromFilename statement" << q.lastError() << q.executedQuery();
1016 q.bindValue(
":filename", filename);
1017 q.bindValue(
":resourceType", resourceType);
1022 qWarning() <<
"Could not execute getResourceIdFromFilename statement" << q.lastError() << filename << resourceType;
1028 outResourceId = q.value(
"resources.id").toInt();
1038 bool r = q.prepare(
"SELECT resource_id FROM versioned_resources\n"
1040 ", resource_types\n"
1042 "WHERE versioned_resources.filename = :filename\n"
1043 "AND resources.id = versioned_resources.resource_id\n"
1044 "AND resource_types.id = resources.resource_type_id\n"
1045 "AND resource_types.name = :resourceType\n"
1046 "AND resources.storage_id = storages.id\n"
1047 "AND storages.location = :storageLocation");
1050 qWarning() <<
"Could not prepare getResourceIdFromVersionedFilename statement" << q.lastError() << q.executedQuery();
1055 q.bindValue(
":filename", filename);
1056 q.bindValue(
":resourceType", resourceType);
1061 qWarning() <<
"Could not execute getResourceIdFromVersionedFilename statement" << q.lastError() << filename << resourceType;
1067 outResourceId = q.value(
"resource_id").toInt();
1076 bool r = q.prepare(
"SELECT filename FROM versioned_resources \n"
1077 "WHERE resource_id = :resource_id;");
1080 qWarning() <<
"Could not prepare getAllVersionsLocations statement" << q.lastError();
1084 q.bindValue(
":resource_id", resourceId);
1087 qWarning() <<
"Could not execute getAllVersionsLocations statement" << q.lastError() << resourceId;
1093 outVersionsLocationsList << q.value(
"filename").toString();
1105 qWarning() <<
"KisResourceCacheDb::addResource: The database is not valid";
1109 if (!resource || !resource->valid()) {
1110 qWarning() <<
"KisResourceCacheDb::addResource: The resource is not valid:" << resource->filename();
1118 if (resourceId > -1) {
1123 r = q.prepare(
"INSERT INTO resources \n"
1124 "(storage_id, resource_type_id, name, filename, tooltip, thumbnail, status, temporary, md5sum) \n"
1128 " WHERE location = :storage_location)\n"
1130 " FROM resource_types\n"
1131 " WHERE name = :resource_type)\n"
1141 qWarning() <<
"Could not prepare addResource statement" << q.lastError();
1145 q.bindValue(
":resource_type", resourceType);
1147 q.bindValue(
":name", resource->name());
1148 q.bindValue(
":filename", resource->filename());
1150 QString translationContext;
1153 +
":" + resourceType +
"/" + resource->filename();
1154 }
else if (storage->location() ==
"memory") {
1155 translationContext =
"memory/" + resourceType +
"/" + resource->filename();
1157 else if (resource->filename().endsWith(
".myb", Qt::CaseInsensitive)) {
1158 translationContext =
"./plugins/paintops/mypaint/brushes/" + resource->filename();
1160 translationContext =
"./krita/data/" + resourceType +
"/" + resource->filename();
1164 QByteArray ctx = translationContext.toUtf8();
1165 QString translatedName = i18nc(ctx, resource->name().toUtf8());
1166 if (translatedName == resource->name()) {
1168 QString altName = QFileInfo(resource->filename()).completeBaseName().replace(
'_',
' ');
1169 QString altTranslatedName = i18nc(ctx, altName.toUtf8());
1170 if (altName != altTranslatedName) {
1171 translatedName = altTranslatedName;
1174 q.bindValue(
":tooltip", translatedName);
1178 buf.open(QBuffer::WriteOnly);
1179 resource->image().save(&buf,
"PNG");
1181 q.bindValue(
":thumbnail", buf.data());
1183 q.bindValue(
":status", resource->active());
1184 q.bindValue(
":temporary", (temporary ? 1 : 0));
1185 q.bindValue(
":md5sum", resource->md5Sum());
1189 qWarning() <<
"Could not execute addResource statement" << q.lastError() << q.boundValues();
1194 if (resourceId < 0) {
1196 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: "
1198 << resource->filename()
1204 resource->setResourceId(resourceId);
1207 qWarning() <<
"Could not add resource version" << resource;
1211 if (!resource->metadata().isEmpty()) {
1222 QSqlDatabase::database().transaction();
1224 while (iter->hasNext()) {
1230 int resourceId = -1;
1232 while (verIt->hasNext()) {
1236 if (resource && resource->valid()) {
1237 resource->setVersion(verIt->guessedVersion());
1238 resource->setMD5Sum(storage->resourceMd5(verIt->url()));
1240 if (resourceId < 0) {
1241 if (
addResource(storage, iter->lastModified(), resource, iter->type())) {
1242 resourceId = resource->resourceId();
1244 qWarning() <<
"Could not add resource" << resource->filename() <<
"to the database";
1248 qWarning() <<
"Could not add resource version" << resource->filename() <<
"to the database";
1254 QSqlDatabase::database().commit();
1260 if (resourceId < 0) {
1261 qWarning() <<
"Invalid resource id; cannot remove resource";
1265 bool r = q.prepare(
"UPDATE resources\n"
1266 "SET status = :status\n"
1267 "WHERE id = :resource_id");
1269 qWarning() <<
"Could not prepare removeResource query" << q.lastError();
1271 q.bindValue(
":status", active);
1272 q.bindValue(
":resource_id", resourceId);
1274 qWarning() <<
"Could not update resource" << resourceId <<
"to inactive" << q.lastError();
1286 QFile f(
":/select_tag.sql");
1287 if (f.open(QFile::ReadOnly)) {
1289 if (!q.prepare(f.readAll())) {
1290 qWarning() <<
"Could not read and prepare select_tag.sql" << q.lastError();
1293 q.bindValue(
":url", tag->url());
1294 q.bindValue(
":resource_type", resourceType);
1297 qWarning() <<
"Could not query tags" << q.boundValues() << q.lastError();
1302 qWarning() <<
"Could not find tag" << q.boundValues() << q.lastError();
1306 tagId = q.value(0).toInt();
1313 bool r = q.prepare(
"SELECT resources.id\n"
1315 ", resource_types\n"
1316 "WHERE resources.resource_type_id = resource_types.id\n"
1317 "AND resource_types.name = :resource_type\n"
1318 "AND resources.filename = :resource_filename\n");
1320 qWarning() <<
"Could not prepare tagResource query" << q.lastError();
1324 q.bindValue(
":resource_type", resourceType);
1325 q.bindValue(
":resource_filename", resourceFileName);
1328 qWarning() <<
"Could not execute tagResource statement" << q.boundValues() << q.lastError();
1335 int resourceId = q.value(0).toInt();
1337 if (resourceId < 0) {
1338 qWarning() <<
"Could not find resource to tag" << resourceFileName << resourceType;
1344 if (!q.prepare(
"SELECT COUNT(*)\n"
1345 "FROM resource_tags\n"
1346 "WHERE resource_id = :resource_id\n"
1347 "AND tag_id = :tag_id")) {
1348 qWarning() <<
"Could not prepare tagResource query 2" << q.lastError();
1351 q.bindValue(
":resource_id", resourceId);
1352 q.bindValue(
":tag_id", tagId);
1355 qWarning() <<
"Could not execute tagResource query 2" << q.lastError() << q.boundValues();
1360 int count = q.value(0).toInt();
1368 if (!q.prepare(
"INSERT INTO resource_tags\n"
1369 "(resource_id, tag_id)\n"
1371 "(:resource_id, :tag_id);")) {
1372 qWarning() <<
"Could not prepare tagResource insert statement" << q.lastError();
1376 q.bindValue(
":resource_id", resourceId);
1377 q.bindValue(
":tag_id", tagId);
1380 qWarning() <<
"Could not execute tagResource stagement" << q.boundValues() << q.lastError();
1390 QFile f(
":/select_tag.sql");
1391 if (f.open(QFile::ReadOnly)) {
1393 if (!q.prepare(f.readAll())) {
1394 qWarning() <<
"Could not read and prepare select_tag.sql" << q.lastError();
1397 q.bindValue(
":url", url);
1398 q.bindValue(
":resource_type", resourceType);
1400 qWarning() <<
"Could not query tags" << q.boundValues() << q.lastError();
1404 qWarning() <<
"Could not open select_tag.sql";
1411 if (!q.prepare(
"INSERT INTO tags_storages\n"
1412 "(tag_id, storage_id)\n"
1417 " WHERE url = :url\n"
1418 " AND resource_type_id = (SELECT id \n"
1419 " FROM resource_types\n"
1420 " WHERE name = :resource_type)"
1424 " WHERE location = :storage_location\n"
1427 qWarning() <<
"Could not prepare add tag/storage statement" << q.lastError();
1431 q.bindValue(
":url", url);
1432 q.bindValue(
":resource_type", resourceType);
1436 qWarning() <<
"Could not insert tag/storage link" << q.boundValues() << q.lastError();
1445 if (
hasTag(tag->url(), resourceType)) {
1448 if (!q.prepare(
"SELECT storages.location\n"
1449 "FROM tags_storages\n"
1452 "WHERE tags.id = tags_storages.tag_id\n"
1453 "AND storages.id = tags_storages.storage_id\n"
1454 "AND tags.resource_type_id = (SELECT id\n"
1455 " FROM resource_types\n"
1456 " WHERE name = :resource_type)\n"
1457 "AND storages.location = :storage_location\n"
1458 "AND tags.url = :url"))
1460 qWarning() <<
"Could not prepare select tags from tags_storages query" << q.lastError();
1463 q.bindValue(
":url", tag->url());
1464 q.bindValue(
":resource_type", resourceType);
1468 qWarning() <<
"Could not execute tags_storages query" << q.boundValues() << q.lastError();
1484 if (!q.prepare(
"INSERT INTO tags\n"
1485 "(url, name, comment, resource_type_id, active, filename)\n"
1491 " FROM resource_types\n"
1492 " WHERE name = :resource_type)\n"
1496 qWarning() <<
"Could not prepare insert tag statement" << q.lastError();
1500 q.bindValue(
":url", tag->url());
1501 q.bindValue(
":name", tag->name(
false));
1502 q.bindValue(
":comment", tag->comment(
false));
1503 q.bindValue(
":resource_type", resourceType);
1504 q.bindValue(
":filename", tag->filename());
1507 qWarning() <<
"Could not insert tag" << q.boundValues() << q.lastError();
1510 tagId = q.lastInsertId().toInt();
1514 Q_FOREACH(
const QString language, tag->names().keys()) {
1516 QString name = tag->names()[language];
1517 QString comment = name;
1518 if (tag->comments().contains(language)) {
1519 comment = tag->comments()[language];
1523 if (!q.prepare(
"INSERT INTO tag_translations\n"
1535 qWarning() <<
"Could not prepare insert tag_translation query" << q.lastError();
1538 q.bindValue(
":id", tagId);
1539 q.bindValue(
":language", language);
1540 q.bindValue(
":name", name);
1541 q.bindValue(
":comment", comment);
1544 qWarning() <<
"Could not execute insert tag_translation query" << q.lastError() << q.boundValues();
1558 QSqlDatabase::database().transaction();
1560 while(iter->hasNext()) {
1563 if (tag && tag->valid()) {
1564 if (!
addTag(resourceType, storage->location(), tag)) {
1565 qWarning() <<
"Could not add tag" << tag <<
"to the database";
1568 if (!tag->defaultResources().isEmpty()) {
1569 Q_FOREACH(
const QString &resourceFileName, tag->defaultResources()) {
1570 if (!
tagResource(resourceFileName, tag, resourceType)) {
1571 qWarning() <<
"Could not tag resource" << QFileInfo(resourceFileName).baseName() <<
"from" << storage->name() <<
"filename" << resourceFileName <<
"with tag" << iter->tag();
1577 QSqlDatabase::database().commit();
1588 if (!q.prepare(
"SELECT count(*)\n"
1589 "FROM storage_types\n"
1590 "WHERE name = :storage_type\n")) {
1591 qWarning() <<
"Could not prepare select from storage_types query" << q.lastError();
1594 q.bindValue(
":storage_type", name);
1596 qWarning() <<
"Could not execute select from storage_types query" << q.lastError();
1600 int rowCount = q.value(0).toInt();
1606 QFile f(
":/fill_storage_types.sql");
1607 if (f.open(QFile::ReadOnly)) {
1608 QString sql = f.readAll();
1610 q.addBindValue(name);
1612 qWarning() <<
"Could not insert" << name << q.lastError();
1617 qWarning() <<
"Could not open fill_storage_types.sql";
1626 qWarning() <<
"The database is not valid";
1632 r = q.prepare(
"SELECT * FROM storages WHERE location = :location");
1636 qWarning() <<
"Could not select from storages";
1649 r = q.prepare(
"INSERT INTO storages\n "
1650 "(storage_type_id, location, timestamp, pre_installed, active, thumbnail)\n"
1652 "(:storage_type_id, :location, :timestamp, :pre_installed, :active, :thumbnail);");
1655 qWarning() <<
"Could not prepare query" << q.lastError();
1659 const QString sanitizedStorageLocation =
1662 q.bindValue(
":storage_type_id",
static_cast<int>(storage->type()));
1663 q.bindValue(
":location", sanitizedStorageLocation);
1664 q.bindValue(
":timestamp", storage->timestamp().toSecsSinceEpoch());
1665 q.bindValue(
":pre_installed", preinstalled ? 1 : 0);
1669 buf.open(QBuffer::WriteOnly);
1670 storage->thumbnail().save(&buf,
"PNG");
1672 q.bindValue(
":thumbnail", buf.data());
1676 if (!r) qWarning() <<
"Could not execute query" << q.lastError();
1678 if (!q.prepare(
"SELECT id\n"
1680 "WHERE location = :location\n")) {
1681 qWarning() <<
"Could not prepare storage id statement" << q.lastError();
1684 q.bindValue(
":location", sanitizedStorageLocation);
1686 qWarning() <<
"Could not execute storage id statement" << q.boundValues() << q.lastError();
1690 qWarning() <<
"Could not find id for the newly added storage" << q.lastError();
1692 storage->setStorageId(q.value(
"id").toInt());
1699 if (keys.size() > 0 && storage->storageId() >= 0) {
1701 QMap<QString, QVariant> metadata;
1703 Q_FOREACH(
const QString &key, storage->metaDataKeys()) {
1704 metadata[key] = storage->metaData(key);
1713 qWarning() <<
"Failed to add all resources for storage" << storage;
1727 qWarning() <<
"Failed to add all tags for storage" << storage;
1742 KisSqlQueryLoader loader(
":/sql/delete_versioned_resources_for_storage_indirect.sql",
1756 KisSqlQueryLoader loader(
":/sql/delete_versioned_resources_for_storage_direct.sql",
1760 if (loader.
query().numRowsAffected() > 0) {
1761 qWarning() <<
"WARNING: deleteStorage: versioned_resurces table contained resource versions not being "
1762 "present in the main table. Deleted: "
1763 << loader.
query().numRowsAffected();
1777 "DELETE FROM resources\n"
1778 "WHERE storage_id = (SELECT storages.id\n"
1780 " WHERE storages.location = :location)\n",
1798 QVariantList uniqueTagIdsToDelete;
1801 std::copy(unique.begin(), unique.end(), std::back_inserter(uniqueTagIdsToDelete));
1806 "WITH storage_id_query AS (\n"
1807 " SELECT storages.id\n"
1809 " WHERE storages.location = :location)\n"
1810 "DELETE FROM tags_storages\n"
1811 "WHERE storage_id IN storage_id_query\n",
1817 if (!uniqueTagIdsToDelete.isEmpty()) {
1820 "DELETE FROM tag_translations WHERE tag_id = ?",
1822 loader.
query().addBindValue(uniqueTagIdsToDelete);
1828 "DELETE FROM resource_tags WHERE tag_id = ?",
1830 loader.
query().addBindValue(uniqueTagIdsToDelete);
1836 "DELETE FROM tags WHERE id = ?",
1838 loader.
query().addBindValue(uniqueTagIdsToDelete);
1845 "DELETE FROM metadata\n"
1846 "WHERE foreign_id = (SELECT storages.id\n"
1848 " WHERE storages.location = :location)"
1849 "AND table_name = :table;",
1858 "DELETE FROM storages\n"
1859 "WHERE location = :location;",
1865 transactionLock.
commit();
1868 qWarning().noquote() <<
"ERROR: deleteStorage:" << e.
message;
1869 qWarning().noquote() <<
" file:" << e.
filePath;
1873 qWarning().noquote() <<
"ERROR: deleteStorage:" << e.
message;
1874 qWarning().noquote() <<
" file:" << e.
filePath;
1875 qWarning().noquote() <<
" statement:" << e.statementIndex;
1876 qWarning().noquote() <<
" error:" << e.sqlError.text();
1889struct ResourceVersion :
public boost::less_than_comparable<ResourceVersion>
1891 int resourceId = -1;
1893 QDateTime timestamp;
1896 bool operator<(
const ResourceVersion &rhs)
const {
1897 return resourceId < rhs.resourceId ||
1898 (resourceId == rhs.resourceId && version < rhs.version);
1901 struct CompareByResourceId {
1902 bool operator() (
const ResourceVersion &lhs,
const ResourceVersion &rhs)
const {
1903 return lhs.resourceId < rhs.resourceId;
1911QDebug
operator<<(QDebug dbg,
const ResourceVersion &ver)
1913 dbg.nospace() <<
"ResourceVersion("
1914 << ver.resourceId <<
", "
1915 << ver.version <<
", "
1916 << ver.timestamp <<
", "
1929 qWarning() <<
"KisResourceCacheDb::addResource: The database is not valid";
1933 bool success =
true;
1937 if (!q.prepare(
"SELECT id\n"
1941 "WHERE location = :location\n")) {
1942 qWarning() <<
"Could not prepare storage timestamp statement" << q.lastError();
1947 qWarning() <<
"Could not execute storage timestamp statement" << q.boundValues() << q.lastError();
1952 debugResource <<
"Adding storage to the database:" << storage;
1954 qWarning() <<
"Could not add new storage" << storage->name() <<
"to the database";
1960 storage->setStorageId(q.value(
"id").toInt());
1963 QSqlDatabase::database().transaction();
1979 int nextInexistentResourceId = std::numeric_limits<int>::min();
1982 while (iter->hasNext()) {
1985 const int firstResourceVersionPosition = resourcesInStorage.size();
1987 int detectedResourceId = nextInexistentResourceId;
1991 while (verIt->hasNext()) {
1998 QString path = QDir::fromNativeSeparators(verIt->url());
1999 int folderEndIdx = path.indexOf(
"/");
2000 QString properFilenameWithSubfolders = path.right(path.length() - folderEndIdx - 1);
2005 ResourceVersion item;
2006 item.url = verIt->url();
2007 item.version = verIt->guessedVersion();
2010 item.timestamp = QDateTime::fromSecsSinceEpoch(verIt->lastModified().toSecsSinceEpoch());
2012 item.resourceId = id;
2014 if (detectedResourceId < 0 && id >= 0) {
2015 detectedResourceId = id;
2018 resourcesInStorage.append(item);
2025 for (
int i = firstResourceVersionPosition; i < resourcesInStorage.size(); i++) {
2026 if (resourcesInStorage[i].resourceId < 0) {
2027 resourcesInStorage[i].resourceId = detectedResourceId;
2031 nextInexistentResourceId++;
2040 q.setForwardOnly(
true);
2041 if (!q.prepare(
"SELECT versioned_resources.resource_id, versioned_resources.filename, versioned_resources.version, versioned_resources.timestamp\n"
2042 "FROM versioned_resources\n"
2043 ", resource_types\n"
2045 "WHERE resources.resource_type_id = resource_types.id\n"
2046 "AND resources.id = versioned_resources.resource_id\n"
2047 "AND resource_types.name = :resource_type\n"
2048 "AND versioned_resources.storage_id == :storage_id")) {
2049 qWarning() <<
"Could not prepare resource by type query" << q.lastError();
2054 q.bindValue(
":resource_type", resourceType);
2055 q.bindValue(
":storage_id",
int(storage->storageId()));
2058 qWarning() <<
"Could not exec resource by type query" << q.boundValues() << q.lastError();
2064 ResourceVersion item;
2065 item.url = resourceType +
"/" + q.value(1).toString();
2066 item.version = q.value(2).toInt();
2067 item.timestamp = QDateTime::fromSecsSinceEpoch(q.value(3).toInt());
2068 item.resourceId = q.value(0).toInt();
2070 resourcesInDatabase.append(item);
2073 QSet<int> resourceIdForUpdate;
2075 std::sort(resourcesInStorage.begin(), resourcesInStorage.end());
2076 std::sort(resourcesInDatabase.begin(), resourcesInDatabase.end());
2078 auto itA = resourcesInStorage.begin();
2079 auto endA = resourcesInStorage.end();
2081 auto itB = resourcesInDatabase.begin();
2082 auto endB = resourcesInDatabase.end();
2088 while (itA != endA) {
2089 if (itA->resourceId >= 0)
break;
2099 res->setVersion(itA->version);
2100 res->setMD5Sum(storage->resourceMd5(itA->url));
2101 if (!res->valid()) {
2107 const bool retval =
addResource(storage, itA->timestamp, res, resourceType);
2114 const int resourceId = res->resourceId();
2121 auto nextResource = std::upper_bound(itA, endA, *itA, ResourceVersion::CompareByResourceId());
2122 for (
auto it = std::next(itA); it != nextResource; ++it) {
2124 res->setVersion(it->version);
2125 res->setMD5Sum(storage->resourceMd5(it->url));
2126 if (!res->valid()) {
2148 while (itA != endA || itB != endB) {
2149 if ((itA != endA && itB != endB && *itA < *itB) ||
2156 res->setVersion(itA->version);
2157 res->setMD5Sum(storage->resourceMd5(itA->url));
2162 resourceIdForUpdate.insert(itA->resourceId);
2166 }
else if ((itA != endA && itB != endB && *itA > *itB) ||
2172 resourceIdForUpdate.insert(itB->resourceId);
2187 for (
auto it = resourceIdForUpdate.begin(); it != resourceIdForUpdate.end(); ++it) {
2192 QSqlDatabase::database().commit();
2193 debugResource <<
"Synchronizing the storages took" << t.elapsed() <<
"milliseconds for" << storage->location();
2208 "inline://delete_metadata_for_resources_in_memory_storages",
2209 "DELETE FROM metadata\n"
2210 "WHERE foreign_id IN (SELECT id\n"
2212 " WHERE storage_id in (SELECT id\n"
2214 " WHERE storage_type_id == :storage_type))\n"
2215 "AND table_name = :table",
2224 "DELETE FROM metadata\n"
2225 "WHERE foreign_id IN (SELECT id\n"
2227 " WHERE temporary = 1)\n"
2228 "AND table_name = :table",
2235 KisSqlQueryLoader loader(
"inline://delete_versions_of_resources_in_temporary_storages",
2236 "DELETE FROM versioned_resources\n"
2237 "WHERE storage_id in (SELECT id\n"
2239 " WHERE storage_type_id == :storage_type)",
2247 "DELETE FROM versioned_resources\n"
2248 "WHERE resource_id IN (SELECT id FROM resources\n"
2249 " WHERE temporary = 1)",
2255 KisSqlQueryLoader loader(
"inline://delete_current_resources_in_temporary_storages",
2256 "DELETE FROM resources\n"
2257 "WHERE storage_id in (SELECT id\n"
2259 " WHERE storage_type_id == :storage_type)",
2267 "DELETE FROM resources\n"
2268 "WHERE temporary = 1",
2279 "DELETE FROM metadata\n"
2280 "WHERE foreign_id IN (SELECT id\n"
2282 " WHERE storage_type_id == :storage_type)\n"
2283 "AND table_name = :table;",
2292 "DELETE FROM storages\n"
2293 "WHERE storage_type_id == :storage_type\n",
2299 transactionLock.
commit();
2301 qWarning().noquote() <<
"ERROR: failed to execute query:" << e.
message;
2302 qWarning().noquote() <<
" file:" << e.
filePath;
2304 qWarning().noquote() <<
" error:" << e.
sqlError.text();
2312 if (!q.prepare(
"PRAGMA optimize;")) {
2313 qWarning() <<
"Could not prepare query" << q.lastQuery() << q.lastError();
2317 qWarning() <<
"Could not execute query" << q.lastQuery() << q.lastError();
2324 QString(
"PRAGMA foreign_keys = %1").arg(isEnabled ?
"ON" :
"OFF"));
2331 "PRAGMA foreign_keys");
2335 if (loader.
query().first()) {
2336 return loader.
query().value(0).toInt();
2345 bool useForeignKeys =
false;
2346 KisUsageLogger::log(
"INFO: detected stable build of Krita, foreign_keys constraint will be disabled");
2348 bool useForeignKeys =
true;
2349 KisUsageLogger::log(
"INFO: detected unstable build of Krita, foreign_keys constraint will be enabled");
2352 if (qEnvironmentVariableIsSet(
"KRITA_OVERRIDE_USE_FOREIGN_KEYS")) {
2353 useForeignKeys = qEnvironmentVariableIntValue(
"KRITA_OVERRIDE_USE_FOREIGN_KEYS") > 0;
2354 KisUsageLogger::log(
"INFO: foreign_keys constraint was overridden by KRITA_OVERRIDE_USE_FOREIGN_KEYS: " + QString::number(useForeignKeys));
2360 if (oldForeignKeysState != useForeignKeys) {
2362 "INFO: switch foreign_keys state: " +
2363 QString::number(oldForeignKeysState) +
2365 QString::number(useForeignKeys));
2371 qWarning().noquote() <<
"ERROR: failed to execute query:" << e.
message;
2372 qWarning().noquote() <<
" file:" << e.
filePath;
2374 qWarning().noquote() <<
" error:" << e.
sqlError.text();
2384 if (!q.prepare(
"SELECT count(*)\n"
2385 "FROM resource_types\n"
2386 "WHERE name = :resource_type\n")) {
2387 qWarning() <<
"Could not prepare select from resource_types query" << q.lastError();
2390 q.bindValue(
":resource_type", resourceType);
2392 qWarning() <<
"Could not execute select from resource_types query" << q.lastError();
2396 int rowCount = q.value(0).toInt();
2402 QFile f(
":/fill_resource_types.sql");
2403 if (f.open(QFile::ReadOnly)) {
2404 QString sql = f.readAll();
2406 q.addBindValue(resourceType);
2408 qWarning() <<
"Could not insert" << resourceType << q.lastError();
2413 qWarning() <<
"Could not open fill_resource_types.sql";
2419 QMap<QString, QVariant> map;
2422 q.setForwardOnly(
true);
2423 if (!q.prepare(
"SELECT key\n"
2426 "WHERE foreign_id = :id\n"
2427 "AND table_name = :table")) {
2428 qWarning() <<
"Could not prepare metadata query" << q.lastError();
2432 q.bindValue(
":id",
id);
2433 q.bindValue(
":table", tableName);
2436 qWarning() <<
"Could not execute metadata query" << q.lastError();
2441 QString key = q.value(0).toString();
2442 QByteArray ba = q.value(1).toByteArray();
2443 if (!ba.isEmpty()) {
2444 QDataStream ds(QByteArray::fromBase64(ba));
2446 ds.setVersion(QDataStream::Qt_5_15);
2457 QSqlDatabase::database().transaction();
2461 if (!q.prepare(
"DELETE FROM metadata\n"
2462 "WHERE foreign_id = :id\n"
2463 "AND table_name = :table\n")) {
2464 QSqlDatabase::database().rollback();
2465 qWarning() <<
"Could not prepare delete metadata query" << q.lastError();
2469 q.bindValue(
":id",
id);
2470 q.bindValue(
":table", tableName);
2473 QSqlDatabase::database().rollback();
2474 qWarning() <<
"Could not execute delete metadata query" << q.lastError();
2481 QSqlDatabase::database().commit();
2484 QSqlDatabase::database().rollback();
2493 if (!q.prepare(
"INSERT INTO metadata\n"
2494 "(foreign_id, table_name, key, value)\n"
2496 "(:id, :table, :key, :value)")) {
2497 QSqlDatabase::database().rollback();
2498 qWarning() <<
"Could not create insert metadata query" << q.lastError();
2502 QMap<QString, QVariant>::const_iterator iter = map.cbegin();
2503 while (iter != map.cend()) {
2504 q.bindValue(
":id",
id);
2505 q.bindValue(
":table", tableName);
2506 q.bindValue(
":key", iter.key());
2508 QVariant
v = iter.value();
2509 if (!
v.isNull() &&
v.isValid()) {
2511 QDataStream ds(&ba, QIODevice::WriteOnly);
2512 ds.setVersion(QDataStream::Qt_5_15);
2515 q.bindValue(
":value", QString::fromLatin1(ba));
2518 qWarning() <<
"Could not insert metadata" << q.lastError();
2529 auto deleteMetadataForType = [] (
const QString &tableName) {
2530 KisSqlQueryLoader loader(
"inline://delete_orphaned_records (" + tableName +
")",
2531 QString(
"DELETE FROM metadata\n"
2532 "WHERE foreign_id NOT IN (SELECT id FROM %1)\n"
2533 "AND table_name = \"%1\"\n")
2537 if (loader.
query().numRowsAffected() > 0) {
2538 qWarning().noquote().nospace() <<
"WARNING: orphaned metadata records were found for " << tableName <<
"!";
2539 qWarning().noquote().nospace() <<
" Num records removed: " << loader.
query().numRowsAffected();
2549 transactionLock.
commit();
2552 qWarning().noquote() <<
"ERROR: failed to execute query:" << e.
message;
2553 qWarning().noquote() <<
" file:" << e.
filePath;
2555 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 std::pair< QVector< int >, QVector< int > > tagsForStorage(const QString &resourceType, const QString &storageLocation)
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)
const QString PaintOpPresets