SQLiteIndex.cpp (15094B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 #include "pch.h" 4 #include "SQLiteIndex.h" 5 #include <winget/SQLiteStorageBase.h> 6 #include "ArpVersionValidation.h" 7 #include <winget/ManifestYamlParser.h> 8 9 namespace AppInstaller::Repository::Microsoft 10 { 11 SQLiteIndex SQLiteIndex::CreateNew(const std::string& filePath, SQLite::Version version, CreateOptions options) 12 { 13 AICLI_LOG(Repo, Info, << "Creating new SQLite Index with version [" << version << "] at '" << filePath << "'"); 14 SQLiteIndex result{ filePath, version }; 15 16 SQLite::Savepoint savepoint = SQLite::Savepoint::Create(result.m_dbconn, "sqliteindex_createnew"); 17 18 // Use calculated version, as incoming version could be 'latest' 19 result.m_version.SetSchemaVersion(result.m_dbconn); 20 21 result.m_interface->CreateTables(result.m_dbconn, options); 22 23 result.SetLastWriteTime(); 24 25 savepoint.Commit(); 26 27 return result; 28 } 29 30 SQLiteIndex SQLiteIndex::Open(const std::string& filePath, OpenDisposition disposition, Utility::ManagedFile&& indexFile) 31 { 32 return { filePath, disposition, std::move(indexFile) }; 33 } 34 35 SQLiteIndex SQLiteIndex::CopyFrom(const std::string& filePath, SQLiteIndex& source) 36 { 37 return { filePath, source }; 38 } 39 40 SQLiteIndex::SQLiteIndex(const std::string& target, const SQLite::Version& version) : SQLiteStorageBase(target, version) 41 { 42 m_dbconn.EnableICU(); 43 m_interface = Schema::CreateISQLiteIndex(version); 44 m_version = m_interface->GetVersion(); 45 SetDatabaseFilePath(target); 46 } 47 48 SQLiteIndex::SQLiteIndex(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile) : 49 SQLiteStorageBase(target, disposition, std::move(indexFile)) 50 { 51 m_dbconn.EnableICU(); 52 AICLI_LOG(Repo, Info, << "Opened SQLite Index with version [" << m_version << "], last write [" << GetLastWriteTime() << "]"); 53 m_interface = Schema::CreateISQLiteIndex(m_version); 54 THROW_HR_IF(APPINSTALLER_CLI_ERROR_CANNOT_WRITE_TO_UPLEVEL_INDEX, disposition == SQLiteStorageBase::OpenDisposition::ReadWrite && m_version != m_interface->GetVersion()); 55 SetDatabaseFilePath(target); 56 } 57 58 SQLiteIndex::SQLiteIndex(const std::string& target, SQLiteIndex& source) : 59 SQLiteStorageBase(target, source) 60 { 61 m_dbconn.EnableICU(); 62 m_interface = Schema::CreateISQLiteIndex(m_version); 63 SetDatabaseFilePath(target); 64 } 65 66 void SQLiteIndex::SetDatabaseFilePath(const std::string& target) 67 { 68 if (target != SQLITE_MEMORY_DB_CONNECTION_TARGET) 69 { 70 m_contextData.Add<Schema::Property::DatabaseFilePath>(Utility::ConvertToUTF16(target)); 71 } 72 } 73 74 #ifndef AICLI_DISABLE_TEST_HOOKS 75 void SQLiteIndex::ForceVersion(const SQLite::Version& version) 76 { 77 m_interface = Schema::CreateISQLiteIndex(version); 78 } 79 80 SQLite::Version SQLiteIndex::GetLatestVersion() 81 { 82 return Schema::CreateISQLiteIndex(SQLite::Version::Latest())->GetVersion(); 83 } 84 85 const Schema::SQLiteIndexContextData& SQLiteIndex::GetContextData() const 86 { 87 return m_contextData; 88 } 89 #endif 90 91 SQLiteIndex::IdType SQLiteIndex::AddManifest(const std::filesystem::path& manifestPath, const std::filesystem::path& relativePath) 92 { 93 AICLI_LOG(Repo, Verbose, << "Adding manifest from file [" << manifestPath << "]"); 94 95 Manifest::Manifest manifest = Manifest::YamlParser::CreateFromPath(manifestPath); 96 return AddManifestInternal(manifest, relativePath); 97 } 98 99 SQLiteIndex::IdType SQLiteIndex::AddManifest(const Manifest::Manifest& manifest, const std::filesystem::path& relativePath) 100 { 101 return AddManifestInternal(manifest, relativePath); 102 } 103 104 SQLiteIndex::IdType SQLiteIndex::AddManifest(const Manifest::Manifest& manifest) 105 { 106 return AddManifestInternal(manifest, {}); 107 } 108 109 SQLiteIndex::IdType SQLiteIndex::AddManifestInternal(const Manifest::Manifest& manifest, const std::optional<std::filesystem::path>& relativePath) 110 { 111 std::lock_guard<std::mutex> lockInterface{ *m_interfaceLock }; 112 return AddManifestInternalHoldingLock(manifest, relativePath); 113 } 114 115 SQLiteIndex::IdType SQLiteIndex::AddManifestInternalHoldingLock(const Manifest::Manifest& manifest, const std::optional<std::filesystem::path>& relativePath) 116 { 117 AICLI_LOG(Repo, Verbose, << "Adding manifest for [" << manifest.Id << ", " << manifest.Version << "] at relative path [" << relativePath.value_or("") << "]"); 118 119 SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "sqliteindex_addmanifest"); 120 121 IdType result = m_interface->AddManifest(m_dbconn, manifest, relativePath); 122 123 SetLastWriteTime(); 124 125 savepoint.Commit(); 126 127 return result; 128 } 129 130 bool SQLiteIndex::UpdateManifest(const std::filesystem::path& manifestPath, const std::filesystem::path& relativePath) 131 { 132 AICLI_LOG(Repo, Verbose, << "Updating manifest from file [" << manifestPath << "]"); 133 134 Manifest::Manifest manifest = Manifest::YamlParser::CreateFromPath(manifestPath); 135 return UpdateManifestInternal(manifest, relativePath); 136 } 137 138 bool SQLiteIndex::UpdateManifest(const Manifest::Manifest& manifest, const std::filesystem::path& relativePath) 139 { 140 return UpdateManifestInternal(manifest, relativePath); 141 } 142 143 bool SQLiteIndex::UpdateManifest(const Manifest::Manifest& manifest) 144 { 145 return UpdateManifestInternal(manifest, {}); 146 } 147 148 bool SQLiteIndex::UpdateManifestInternal(const Manifest::Manifest& manifest, const std::optional<std::filesystem::path>& relativePath) 149 { 150 std::lock_guard<std::mutex> lockInterface{ *m_interfaceLock }; 151 return UpdateManifestInternalHoldingLock(manifest, relativePath); 152 } 153 154 bool SQLiteIndex::UpdateManifestInternalHoldingLock(const Manifest::Manifest& manifest, const std::optional<std::filesystem::path>& relativePath) 155 { 156 AICLI_LOG(Repo, Verbose, << "Updating manifest for [" << manifest.Id << ", " << manifest.Version << "] at relative path [" << relativePath.value_or("") << "]"); 157 158 SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "sqliteindex_updatemanifest"); 159 160 bool result = m_interface->UpdateManifest(m_dbconn, manifest, relativePath).first; 161 162 if (result) 163 { 164 SetLastWriteTime(); 165 166 savepoint.Commit(); 167 } 168 169 return result; 170 } 171 172 bool SQLiteIndex::AddOrUpdateManifest(const std::filesystem::path& manifestPath, const std::filesystem::path& relativePath) 173 { 174 AICLI_LOG(Repo, Verbose, << "Adding or Updating manifest from file [" << manifestPath << "]"); 175 176 Manifest::Manifest manifest = Manifest::YamlParser::CreateFromPath(manifestPath); 177 return AddOrUpdateManifestInternal(manifest, relativePath); 178 } 179 180 bool SQLiteIndex::AddOrUpdateManifest(const Manifest::Manifest& manifest, const std::filesystem::path& relativePath) 181 { 182 return AddOrUpdateManifestInternal(manifest, relativePath); 183 } 184 185 bool SQLiteIndex::AddOrUpdateManifest(const Manifest::Manifest& manifest) 186 { 187 return AddOrUpdateManifestInternal(manifest, {}); 188 } 189 190 bool SQLiteIndex::AddOrUpdateManifestInternal(const Manifest::Manifest& manifest, const std::optional<std::filesystem::path>& relativePath) 191 { 192 std::lock_guard<std::mutex> lockInterface{ *m_interfaceLock }; 193 AICLI_LOG(Repo, Verbose, << "Adding or Updating manifest for [" << manifest.Id << ", " << manifest.Version << "] at relative path [" << relativePath.value_or("") << "]"); 194 195 if (m_interface->GetManifestIdByManifest(m_dbconn, manifest)) 196 { 197 UpdateManifestInternalHoldingLock(manifest, relativePath); 198 return false; 199 } 200 else 201 { 202 AddManifestInternalHoldingLock(manifest, relativePath); 203 return true; 204 } 205 } 206 207 void SQLiteIndex::RemoveManifest(const std::filesystem::path& manifestPath, const std::filesystem::path& relativePath) 208 { 209 AICLI_LOG(Repo, Verbose, << "Removing manifest from file [" << manifestPath << "]"); 210 211 Manifest::Manifest manifest = Manifest::YamlParser::CreateFromPath(manifestPath); 212 RemoveManifest(manifest, relativePath); 213 } 214 215 void SQLiteIndex::RemoveManifest(const Manifest::Manifest& manifest, const std::filesystem::path& relativePath) 216 { 217 AICLI_LOG(Repo, Verbose, << "Removing manifest for [" << manifest.Id << ", " << manifest.Version << "] at relative path [" << relativePath << "]"); 218 RemoveManifest(manifest); 219 } 220 221 void SQLiteIndex::RemoveManifest(const Manifest::Manifest& manifest) 222 { 223 std::lock_guard<std::mutex> lockInterface{ *m_interfaceLock }; 224 SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "sqliteindex_removemanifest"); 225 226 m_interface->RemoveManifest(m_dbconn, manifest); 227 228 SetLastWriteTime(); 229 230 savepoint.Commit(); 231 } 232 233 void SQLiteIndex::RemoveManifestById(IdType manifestId) 234 { 235 SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "SQLiteIndex_RemoveManifestById"); 236 237 m_interface->RemoveManifestById(m_dbconn, manifestId); 238 239 SetLastWriteTime(); 240 241 savepoint.Commit(); 242 } 243 244 void SQLiteIndex::PrepareForPackaging() 245 { 246 std::lock_guard<std::mutex> lockInterface{ *m_interfaceLock }; 247 AICLI_LOG(Repo, Info, << "Preparing index for packaging"); 248 249 m_interface->PrepareForPackaging(Schema::SQLiteIndexContext{ m_dbconn, m_contextData }); 250 } 251 252 bool SQLiteIndex::CheckConsistency(bool log) const 253 { 254 std::lock_guard<std::mutex> lockInterface{ *m_interfaceLock }; 255 AICLI_LOG(Repo, Info, << "Checking index consistency..."); 256 257 bool result = m_interface->CheckConsistency(m_dbconn, log); 258 259 AICLI_LOG(Repo, Info, << "...index *WAS" << (result ? "*" : " NOT*") << " consistent."); 260 261 return result; 262 } 263 264 Schema::ISQLiteIndex::SearchResult SQLiteIndex::Search(const SearchRequest& request) const 265 { 266 std::lock_guard<std::mutex> lockInterface{ *m_interfaceLock }; 267 AICLI_LOG(Repo, Verbose, << "Performing search: " << request.ToString()); 268 269 return m_interface->Search(m_dbconn, request); 270 } 271 272 std::optional<std::string> SQLiteIndex::GetPropertyByPrimaryId(IdType primaryId, PackageVersionProperty property) const 273 { 274 std::lock_guard<std::mutex> lockInterface{ *m_interfaceLock }; 275 return m_interface->GetPropertyByPrimaryId(m_dbconn, primaryId, property); 276 } 277 278 std::vector<std::string> SQLiteIndex::GetMultiPropertyByPrimaryId(IdType primaryId, PackageVersionMultiProperty property) const 279 { 280 std::lock_guard<std::mutex> lockInterface{ *m_interfaceLock }; 281 return m_interface->GetMultiPropertyByPrimaryId(m_dbconn, primaryId, property); 282 } 283 284 std::optional<SQLiteIndex::IdType> SQLiteIndex::GetManifestIdByKey(IdType id, std::string_view version, std::string_view channel) const 285 { 286 std::lock_guard<std::mutex> lockInterface{ *m_interfaceLock }; 287 return m_interface->GetManifestIdByKey(m_dbconn, id, version, channel); 288 } 289 290 std::optional<SQLiteIndex::IdType> SQLiteIndex::GetManifestIdByManifest(const Manifest::Manifest& manifest) const 291 { 292 return m_interface->GetManifestIdByManifest(m_dbconn, manifest); 293 } 294 295 std::vector<SQLiteIndex::VersionKey> SQLiteIndex::GetVersionKeysById(IdType id) const 296 { 297 std::lock_guard<std::mutex> lockInterface{ *m_interfaceLock }; 298 return m_interface->GetVersionKeysById(m_dbconn, id); 299 } 300 301 SQLiteIndex::MetadataResult SQLiteIndex::GetMetadataByManifestId(SQLite::rowid_t manifestId) const 302 { 303 std::lock_guard<std::mutex> lockInterface{ *m_interfaceLock }; 304 return m_interface->GetMetadataByManifestId(m_dbconn, manifestId); 305 } 306 307 void SQLiteIndex::SetMetadataByManifestId(IdType manifestId, PackageVersionMetadata metadata, std::string_view value) 308 { 309 std::lock_guard<std::mutex> lockInterface{ *m_interfaceLock }; 310 m_interface->SetMetadataByManifestId(m_dbconn, manifestId, metadata, value); 311 } 312 313 Utility::NormalizedName SQLiteIndex::NormalizeName(std::string_view name, std::string_view publisher) const 314 { 315 std::lock_guard<std::mutex> lockInterface{ *m_interfaceLock }; 316 return m_interface->NormalizeName(name, publisher); 317 } 318 319 std::set<std::pair<SQLite::rowid_t, Utility::NormalizedString>> SQLiteIndex::GetDependenciesByManifestRowId(SQLite::rowid_t manifestRowId) const 320 { 321 return m_interface->GetDependenciesByManifestRowId(m_dbconn, manifestRowId); 322 } 323 324 std::vector<std::pair<SQLite::rowid_t, Utility::NormalizedString>> SQLiteIndex::GetDependentsById(AppInstaller::Manifest::string_t packageId) const 325 { 326 return m_interface->GetDependentsById(m_dbconn, packageId); 327 } 328 329 bool SQLiteIndex::MigrateTo(SQLite::Version version) 330 { 331 std::lock_guard<std::mutex> lockInterface{ *m_interfaceLock }; 332 SQLite::Savepoint savepoint = SQLite::Savepoint::Create(m_dbconn, "sqliteindex_migrate_to"); 333 334 AICLI_LOG(Repo, Info, << "Attempting to migrate index from [" << m_interface->GetVersion() << "] to [" << version << "]..."); 335 std::unique_ptr<Schema::ISQLiteIndex> newInterface = Schema::CreateISQLiteIndex(version); 336 337 bool result = newInterface->MigrateFrom(m_dbconn, m_interface.get()); 338 339 AICLI_LOG(Repo, Info, << "...migration was " << (result ? "" : "NOT ") << "successful"); 340 if (result) 341 { 342 version.SetSchemaVersion(m_dbconn); 343 SetLastWriteTime(); 344 savepoint.Commit(); 345 346 m_version = version; 347 m_interface = std::move(newInterface); 348 } 349 350 return result; 351 } 352 353 void SQLiteIndex::SetProperty(Property property, const std::string& value) 354 { 355 std::lock_guard<std::mutex> lockInterface{ *m_interfaceLock }; 356 357 switch (property) 358 { 359 case Property::PackageUpdateTrackingBaseTime: 360 m_interface->SetProperty(m_dbconn, Schema::Property::PackageUpdateTrackingBaseTime, value); 361 break; 362 case Property::IntermediateFileOutputPath: 363 { 364 std::filesystem::path pathValue{ Utility::ConvertToUTF16(value) }; 365 THROW_HR_IF(E_INVALIDARG, pathValue.empty() || pathValue.is_relative()); 366 m_contextData.Add<Schema::Property::IntermediateFileOutputPath>(std::move(pathValue)); 367 } 368 break; 369 } 370 } 371 }