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