File indexing completed on 2025-02-02 05:17:47

0001 /*
0002  *   SPDX-FileCopyrightText: 2015-2016 Ivan Cukic <ivan.cukic@kde.org>
0003  *
0004  *   SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005  */
0006 
0007 #include "ResourcesDatabaseSchema.h"
0008 
0009 #include <QCoreApplication>
0010 #include <QStandardPaths>
0011 #include <QVariant>
0012 
0013 namespace Common
0014 {
0015 namespace ResourcesDatabaseSchema
0016 {
0017 const QString name = QStringLiteral("Resources");
0018 
0019 QString version()
0020 {
0021     return QStringLiteral("2015.02.09");
0022 }
0023 
0024 QStringList schema()
0025 {
0026     // If only we could use initializer lists here ...
0027 
0028     return QStringList()
0029 
0030         << // Schema information table, used for versioning
0031         QStringLiteral(
0032                "CREATE TABLE IF NOT EXISTS SchemaInfo ("
0033                "key text PRIMARY KEY, value text"
0034                ")")
0035 
0036         << QStringLiteral("INSERT OR IGNORE INTO schemaInfo VALUES ('version', '%1')").arg(version())
0037         << QStringLiteral("UPDATE schemaInfo SET value = '%1' WHERE key = 'version'").arg(version())
0038 
0039         << // The ResourceEvent table saves the Opened/Closed event pairs for
0040            // a resource. The Accessed event is mapped to those.
0041            // Focusing events are not stored in order not to get a
0042            // huge database file and to lessen writes to the disk.
0043         QStringLiteral(
0044                "CREATE TABLE IF NOT EXISTS ResourceEvent ("
0045                "usedActivity TEXT, "
0046                "initiatingAgent TEXT, "
0047                "targettedResource TEXT, "
0048                "start INTEGER, "
0049                "end INTEGER "
0050                ")")
0051 
0052         << // The ResourceScoreCache table stores the calculated scores
0053            // for resources based on the recorded events.
0054         QStringLiteral(
0055                "CREATE TABLE IF NOT EXISTS ResourceScoreCache ("
0056                "usedActivity TEXT, "
0057                "initiatingAgent TEXT, "
0058                "targettedResource TEXT, "
0059                "scoreType INTEGER, "
0060                "cachedScore FLOAT, "
0061                "firstUpdate INTEGER, "
0062                "lastUpdate INTEGER, "
0063                "PRIMARY KEY(usedActivity, initiatingAgent, targettedResource)"
0064                ")")
0065 
0066         << // @since 2014.05.05
0067            // The ResourceLink table stores the information, formerly kept by
0068            // Nepomuk, of which resources are linked to which activities.
0069            // The additional features compared to the old days are
0070            // the ability to limit the link to specific applications, and
0071            // to create global links.
0072         QStringLiteral(
0073                "CREATE TABLE IF NOT EXISTS ResourceLink ("
0074                "usedActivity TEXT, "
0075                "initiatingAgent TEXT, "
0076                "targettedResource TEXT, "
0077                "PRIMARY KEY(usedActivity, initiatingAgent, targettedResource)"
0078                ")")
0079 
0080         << // @since 2015.01.18
0081            // The ResourceInfo table stores the collected information about a
0082            // resource that is not agent nor activity related like the
0083            // title and the mime type.
0084            // If these are automatically retrieved (works for files), the
0085            // flag is set to true. This is done for the agents to be able to
0086            // override these.
0087         QStringLiteral(
0088                "CREATE TABLE IF NOT EXISTS ResourceInfo ("
0089                "targettedResource TEXT, "
0090                "title TEXT, "
0091                "mimetype TEXT, "
0092                "autoTitle INTEGER, "
0093                "autoMimetype INTEGER, "
0094                "PRIMARY KEY(targettedResource)"
0095                ")")
0096 
0097         ;
0098 }
0099 
0100 // TODO: This will require some refactoring after we introduce more databases
0101 QString defaultPath()
0102 {
0103     return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/kactivitymanagerd/resources/database");
0104 }
0105 
0106 const char *overrideFlagProperty = "org.kde.KActivities.ResourcesDatabase.overrideDatabase";
0107 const char *overrideFileProperty = "org.kde.KActivities.ResourcesDatabase.overrideDatabaseFile";
0108 
0109 QString path()
0110 {
0111     auto app = QCoreApplication::instance();
0112 
0113     return (app->property(overrideFlagProperty).toBool()) ? app->property(overrideFileProperty).toString() : defaultPath();
0114 }
0115 
0116 void overridePath(const QString &path)
0117 {
0118     auto app = QCoreApplication::instance();
0119 
0120     app->setProperty(overrideFlagProperty, true);
0121     app->setProperty(overrideFileProperty, path);
0122 }
0123 
0124 void initSchema(Database &database)
0125 {
0126     QString dbSchemaVersion;
0127 
0128     auto query = database.execQuery(QStringLiteral("SELECT value FROM SchemaInfo WHERE key = 'version'"),
0129                                     /* ignore error */ true);
0130 
0131     if (query.next()) {
0132         dbSchemaVersion = query.value(0).toString();
0133     }
0134 
0135     // Early bail-out if the schema is up-to-date
0136     if (dbSchemaVersion == version()) {
0137         return;
0138     }
0139 
0140     // Transition to KF5:
0141     // We left the world of Nepomuk, and all the ontologies went
0142     // across the sea to the Undying Lands.
0143     // This needs to be done before executing the schema() queries
0144     // so that we do not create new (empty) tables and block these
0145     // queries from being executed.
0146     if (dbSchemaVersion < QStringLiteral("2014.04.14")) {
0147         database.execQuery(QStringLiteral("ALTER TABLE nuao_DesktopEvent RENAME TO ResourceEvent"),
0148                            /* ignore error */ true);
0149         database.execQuery(QStringLiteral("ALTER TABLE kext_ResourceScoreCache RENAME TO ResourceScoreCache"),
0150                            /* ignore error */ true);
0151     }
0152 
0153     database.execQueries(ResourcesDatabaseSchema::schema());
0154 
0155     // We are asking for trouble. If the database is corrupt,
0156     // some of these should fail.
0157     // WARNING: Sqlite specific!
0158     database.execQueries(QStringList{
0159         ".tables",
0160         "SELECT count(*) FROM SchemaInfo",
0161         "SELECT count(*) FROM ResourceEvent",
0162         "SELECT count(*) FROM ResourceScoreCache",
0163         "SELECT count(*) FROM ResourceLink",
0164         "SELECT count(*) FROM ResourceInfo",
0165     });
0166 
0167     // We can not allow empty fields for activity and agent, they need to
0168     // be at least magic values. These do not change the structure
0169     // of the database, but the old data.
0170     if (dbSchemaVersion < QStringLiteral("2015.02.09")) {
0171         const QString updateActivity = QStringLiteral(
0172             "SET usedActivity=':global' "
0173             "WHERE usedActivity IS NULL OR usedActivity = ''");
0174 
0175         const QString updateAgent = QStringLiteral(
0176             "SET initiatingAgent=':global' "
0177             "WHERE initiatingAgent IS NULL OR initiatingAgent = ''");
0178 
0179         // When the activity field was empty, it meant the file was
0180         // linked to all activities (aka :global)
0181         database.execQuery("UPDATE ResourceLink " + updateActivity);
0182 
0183         // When the agent field was empty, it meant the file was not
0184         // linked to a specified agent (aka :global)
0185         database.execQuery("UPDATE ResourceLink " + updateAgent);
0186 
0187         // These were not supposed to be empty, but in the case they were,
0188         // deal with them as well
0189         database.execQuery("UPDATE ResourceEvent " + updateActivity);
0190         database.execQuery("UPDATE ResourceEvent " + updateAgent);
0191         database.execQuery("UPDATE ResourceScoreCache " + updateActivity);
0192         database.execQuery("UPDATE ResourceScoreCache " + updateAgent);
0193     }
0194 }
0195 
0196 } // namespace Common
0197 } // namespace ResourcesDatabaseSchema