initialization is completed, transaction is over, now enable the foreign_keys constraint if necessary
initialization is completed, transaction is over, now enable the foreign_keys constraint if necessary
143{
144
145
146
155
156 QDir dbLocation(location);
157 if (!dbLocation.exists()) {
158 dbLocation.mkpath(dbLocation.path());
159 }
160
161 std::optional<QSqlDatabase> existingDatabase =
162 QSqlDatabase::database(QSqlDatabase::defaultConnection, false);
163
164 const bool databaseConnectionExists = !QSqlDatabase::connectionNames().isEmpty()
165 && existingDatabase->isValid() && existingDatabase->isOpen();
166
167 if (databaseConnectionExists && existingDatabase->tables().contains("version_information")) {
168 return QSqlError();
169 }
170
171 existingDatabase = std::nullopt;
172
173 QSqlDatabase db;
174
175 if (!databaseConnectionExists) {
176 db = QSqlDatabase::addDatabase(
dbDriver);
178
179 if (!db.open()) {
181 return db.lastError();
182 }
183 } else {
184 db = QSqlDatabase::database();
185 }
186
187
188 QVersionNumber oldSchemaVersionNumber;
190
191
193 << "storage_types"
194 << "resource_types"
195 << "storages"
196 << "tags"
197 << "resources"
198 << "versioned_resources"
199 << "resource_tags"
200 << "metadata"
201 << "tags_storages"
202 << "tag_translations";
203
205
206 {
207 bool allTablesPresent = true;
208 dbTables = db.tables();
209 Q_FOREACH(const QString &table, tables) {
210 if (!dbTables.contains(table)) {
211 allTablesPresent = false;
212 break;
213 }
214 }
215
216 bool schemaIsOutDated = false;
217 QString schemaVersion = "0.0.0";
218 QString kritaVersion = "Unknown";
219 int creationDate = 0;
220
221 if (dbTables.contains("version_information")) {
222
223
224 {
225 QSqlQuery q(
226 "SELECT database_version\n"
227 ", krita_version\n"
228 ", creation_date\n"
229 "FROM version_information\n"
230 "ORDER BY id\n"
231 "DESC\n"
232 "LIMIT 1;\n");
233
234 if (!q.exec()) {
235 warnDbMigration <<
"Could not retrieve version information from the database." << q.lastError();
236 abort();
237 }
238 q.first();
239 schemaVersion = q.value(0).toString();
240 kritaVersion = q.value(1).toString();
241 creationDate = q.value(2).toInt();
242 }
243
244 oldSchemaVersionNumber = QVersionNumber::fromString(schemaVersion);
246
247 if (QVersionNumber::compare(oldSchemaVersionNumber, newSchemaVersionNumber) != 0) {
248
249 infoDbMigration <<
"Old schema:" << schemaVersion <<
"New schema:" << newSchemaVersionNumber;
250
251 schemaIsOutDated = true;
253
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) {
257
258 bool from14to15 = oldSchemaVersionNumber == QVersionNumber::fromString("0.0.14");
259
260 bool from15to16 = oldSchemaVersionNumber == QVersionNumber::fromString("0.0.14")
261 || oldSchemaVersionNumber == QVersionNumber::fromString("0.0.15");
262
263 bool from16to17 = oldSchemaVersionNumber == QVersionNumber::fromString("0.0.14")
264 || oldSchemaVersionNumber == QVersionNumber::fromString("0.0.15")
265 || oldSchemaVersionNumber == QVersionNumber::fromString("0.0.16");
266
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");
271
273
274 bool success = true;
275 if (from14to15) {
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) {
281 success = false;
282 }
283 }
284 if (success && from15to16) {
286
288
289 Q_FOREACH(const QString &index, indexes) {
291 QString("Create index for %1").arg(index));
292 if (error.type() != QSqlError::NoError) {
293 success = false;
294 }
295 }
296 }
297
298 if (success && from16to17) {
300 "Create index for resources_signature");
301 if (error.type() != QSqlError::NoError) {
302 success = false;
303 }
304 }
305
306 if (success && from17to18) {
307 {
309 "Cleanup and deduplicate metadata table");
310 if (error.type() != QSqlError::NoError) {
311 success = false;
312 }
313 }
314 if (success) {
315 QSqlError error =
runUpdateScriptFile(
":/0_0_18_0002_update_metadata_table_constraints.sql",
316 "Update metadata table constraints");
317 if (error.type() != QSqlError::NoError) {
318 success = false;
319 }
320 }
321 if (success) {
323 "Create index for metadata_key");
324 if (error.type() != QSqlError::NoError) {
325 success = false;
326 }
327 }
328 }
329
330 if (success) {
332 success = false;
333 }
334
335 transactionLock.commit();
336
337 if (success) {
339 "Vacuum database after updating schema");
340 if (error.type() != QSqlError::NoError) {
341 success = false;
342 }
343 }
344 } else {
345 transactionLock.rollback();
346 }
347
348 schemaIsOutDated = !success;
349
350 }
351
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) {
356 }
357 db.close();
359 db.open();
360 }
361 }
362
363 }
364
365 if (allTablesPresent && !schemaIsOutDated) {
366 KisUsageLogger::log(QString(
"Database is up to date. Version: %1, created by Krita %2, at %3")
367 .arg(schemaVersion)
368 .arg(kritaVersion)
369 .arg(QDateTime::fromSecsSinceEpoch(creationDate).
toString()));
370
374
375 return QSqlError();
376 }
377 }
378
380 .arg(oldSchemaVersionNumber.toString().isEmpty() ? QString(
"database didn't exist") : (
"old schema version: " + oldSchemaVersionNumber.
toString()))
381 .arg(
"new schema version: " + newSchemaVersionNumber.
toString()));
382
384
385
386 Q_FOREACH(const QString &table, tables) {
387 QSqlError error =
389 if (error.type() != QSqlError::NoError) {
390 return error;
391 }
392 }
393
394 {
395
396 QSqlError error =
runUpdateScriptFile(
":/0_0_18_0002_update_metadata_table_constraints.sql",
397 "Update metadata table constraints");
398
399 if (error.type() != QSqlError::NoError) {
400 return error;
401 }
402 }
403
404
406
407
408 indexes << "storages" << "versioned_resources" << "tags" << "resources" << "tag_translations" << "resource_tags";
409
410
411 indexes << "resources_signature";
412
413
414 indexes << "metadata_key";
415
416 Q_FOREACH(const QString &index, indexes) {
418 QString("Create index for %1").arg(index));
419 if (error.type() != QSqlError::NoError) {
420 return error;
421 }
422 }
423
424
425 {
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);
431 QSqlQuery q(sql);
432 q.addBindValue(originType);
433 if (!q.exec()) {
434 warnDbMigration <<
"Could execute DB update step:" << updateStep << q.lastError();
436 return db.lastError();
437 }
439 }
440 }
441 else {
442 return QSqlError("Error executing SQL", QString("Could not find SQL fill_storage_types.sql."), QSqlError::StatementError);
443 }
444 }
445
446 {
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);
452 QSqlQuery q(sql);
453 q.addBindValue(resourceType);
454 if (!q.exec()) {
455 warnDbMigration <<
"Could execute DB update step:" << updateStep << q.lastError();
457 return db.lastError();
458 }
460 }
461 }
462 else {
463 return QSqlError("Error executing SQL", QString("Could not find SQL fill_resource_types.sql."), QSqlError::StatementError);
464 }
465 }
466
468 return QSqlError("Error executing SQL", QString("Could not update schema version."), QSqlError::StatementError);
469 }
470
471 transactionLock.commit();
472
476
477 return QSqlError();
478}
QList< QString > QStringList
bool updateSchemaVersion()
QSqlError runUpdateScriptFile(const QString &path, const QString &message)
QSqlError runUpdateScript(const QString &script, const QString &message)
static bool numberedBackupFile(const QString &filename, const QString &backupDir=QString(), const QString &backupExtension=QStringLiteral("~"), const uint maxBackups=10)
static const QString resourceCacheDbFilename
filename of the database
static QStringList storageTypes
kinds of places where resources can be stored
static const QString databaseVersion
current schema version
static void synchronizeForeignKeysState()
static KisResourceLoaderRegistry * instance()
static void saveTags()
saveTags saves all tags to .tag files in the resource folder
static KisResourceLocator * instance()
static QString storageTypeToUntranslatedString(StorageType storageType)
static void log(const QString &message)
Logs with date/time.
QString toString(const QString &value)