winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 83cba4835230e168dc9e9c5537b4bbb5934e2ddb
parent 5c2ff990d93536bcc0a0db25abc0d3e9ec6900aa
Author: yao-msft <50888816+yao-msft@users.noreply.github.com>
Date:   Wed, 13 Jul 2022 16:19:00 -0700

Fix WinGetUtil index operations for manifest dependencies without min version (#2337)


Diffstat:
Msrc/AppInstallerCLITests/SQLiteIndex.cpp | 12+++++++++---
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/Interface_1_0.cpp | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_4/DependenciesTable.cpp | 35++++++++++++++++++++++-------------
Msrc/AppInstallerRepositoryCore/PackageDependenciesValidation.cpp | 78+++++++++++++++++++++++++++++++++++++++++-------------------------------------
4 files changed, 73 insertions(+), 54 deletions(-)

diff --git a/src/AppInstallerCLITests/SQLiteIndex.cpp b/src/AppInstallerCLITests/SQLiteIndex.cpp @@ -485,7 +485,7 @@ TEST_CASE("SQLiteIndex_VersionReferencedByDependenciesClearsUnusedVersionAndKeep } } -TEST_CASE("SQLiteIndex_AddManifestWithDependencies", "[sqliteindex][V1_4]") +TEST_CASE("SQLiteIndex_AddUpdateRemoveManifestWithDependencies", "[sqliteindex][V1_4]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); @@ -503,6 +503,8 @@ TEST_CASE("SQLiteIndex_AddManifestWithDependencies", "[sqliteindex][V1_4]") manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest2.Id, "1.0.0")); index.AddManifest(manifest, GetPathFromManifest(manifest)); + index.UpdateManifest(manifest, GetPathFromManifest(manifest)); + index.RemoveManifest(manifest); } TEST_CASE("SQLiteIndex_AddManifestWithDependencies_MissingPackage", "[sqliteindex][V1_4]") @@ -525,7 +527,7 @@ TEST_CASE("SQLiteIndex_AddManifestWithDependencies_MissingPackage", "[sqliteinde REQUIRE_THROWS_HR(index.AddManifest(manifest, GetPathFromManifest(manifest)), APPINSTALLER_CLI_ERROR_MISSING_PACKAGE); } -TEST_CASE("SQLiteIndex_AddManifestWithDependencies_MissingVersion", "[sqliteindex][V1_4]") +TEST_CASE("SQLiteIndex_AddUpdateRemoveManifestWithDependencies_MissingVersion", "[sqliteindex][V1_4]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); @@ -543,9 +545,11 @@ TEST_CASE("SQLiteIndex_AddManifestWithDependencies_MissingVersion", "[sqliteinde manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest2.Id, "0.0.2")); index.AddManifest(manifest, GetPathFromManifest(manifest)); + index.UpdateManifest(manifest, GetPathFromManifest(manifest)); + index.RemoveManifest(manifest); } -TEST_CASE("SQLiteIndex_AddManifestWithDependencies_EmptyManifestVersion", "[sqliteindex][V1_4]") +TEST_CASE("SQLiteIndex_AddUpdateRemoveManifestWithDependencies_EmptyManifestVersion", "[sqliteindex][V1_4]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); @@ -563,6 +567,8 @@ TEST_CASE("SQLiteIndex_AddManifestWithDependencies_EmptyManifestVersion", "[sqli manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest2.Id)); index.AddManifest(manifest, GetPathFromManifest(manifest)); + index.UpdateManifest(manifest, GetPathFromManifest(manifest)); + index.RemoveManifest(manifest); } TEST_CASE("SQLiteIndex_DependenciesTable_CheckConsistency", "[sqliteindex][V1_4]") diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/Interface_1_0.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/Interface_1_0.cpp @@ -63,7 +63,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 std::optional<SQLite::rowid_t> channelIdOpt = ChannelTable::SelectIdByValue(connection, channel, true); if (!channelIdOpt && !channel.empty()) { - // If an empty channel was given but none was found, we will just not filter on channel. + // If a non-empty channel was given but none was found, we will just not filter on channel. AICLI_LOG(Repo, Info, << "Did not find a Channel { " << channel << " }"); return {}; } diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_4/DependenciesTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_4/DependenciesTable.cpp @@ -138,7 +138,6 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_4 .Values(Unbound, Unbound, Unbound); SQLite::Statement insert = insertBuilder.Prepare(connection); - for (const auto& dep : dependenciesTableRows) { insert.Reset(); @@ -296,8 +295,8 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_4 } ); - bool tableUpdated = InsertManifestDependencies(connection, toAddDependencies); - tableUpdated = RemoveDependenciesByRowIds(connection, toRemoveDependencies) || tableUpdated; + bool tableUpdated = RemoveDependenciesByRowIds(connection, toRemoveDependencies); + tableUpdated = InsertManifestDependencies(connection, toAddDependencies) || tableUpdated; savepoint.Commit(); return tableUpdated; @@ -327,17 +326,18 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_4 StatementBuilder builder; - // Find all manifest that depend on this package. - // SELECT [dep].[manifest], [pId].[id], [minV].[version] FROM [dependencies] AS [dep] - // JOIN [versions] AS [minV] ON [dep].[min_version] = [minV].[rowid] + // Find all manifest that depend on this package. Use outer join for joining version table as min_version may be NULL. + // SELECT [dep].[manifest], [dep].[min_version], [pId].[id], [minV].[version] FROM [dependencies] AS [dep] + // LEFT OUTER JOIN [versions] AS [minV] ON [dep].[min_version] = [minV].[rowid] // JOIN [ids] AS [pId] ON [pId].[rowid] = [dep].[package_id] // WHERE [pId].[id] = ? builder.Select() .Column(QCol(depTableAlias, s_DependenciesTable_Manifest_Column_Name)) + .Column(QCol(depTableAlias, s_DependenciesTable_MinVersion_Column_Name)) .Column(QCol(packageIdAlias, IdTable::ValueName())) .Column(QCol(minVersionAlias, VersionTable::ValueName())) .From({ s_DependenciesTable_Table_Name }).As(depTableAlias) - .Join({ VersionTable::TableName() }).As(minVersionAlias) + .LeftOuterJoin({ VersionTable::TableName() }).As(minVersionAlias) .On(QCol(depTableAlias, s_DependenciesTable_MinVersion_Column_Name), QCol(minVersionAlias, SQLite::RowIDName)) .Join({ IdTable::TableName() }).As(packageIdAlias) .On(QCol(packageIdAlias, SQLite::RowIDName), QCol(depTableAlias, s_DependenciesTable_PackageId_Column_Name)) @@ -350,8 +350,14 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_4 while (stmt.Step()) { + Utility::NormalizedString version = ""; + if (!stmt.GetColumnIsNull(1)) + { + // If min_version is not NULL, use the corresponding value from Version table. + version = stmt.GetColumn<std::string>(3); + } resultSet.emplace_back( - std::make_pair(stmt.GetColumn<SQLite::rowid_t>(0), Utility::NormalizedString(stmt.GetColumn<std::string>(2)))); + std::make_pair(stmt.GetColumn<SQLite::rowid_t>(0), std::move(version))); } return resultSet; @@ -366,14 +372,16 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_4 std::set<std::pair<SQLite::rowid_t, Utility::NormalizedString>> resultSet; - // SELECT [dep].[package_id], [minV].[version] FROM [dependencies] AS [dep] - // JOIN [versions] AS [minV] ON [minV].[rowid] = [dep].[min_version] + // Use Outer join since min_version could have NULL value. + // SELECT [dep].[package_id], [dep].[min_version], [minV].[version] FROM [dependencies] AS [dep] + // LEFT OUTER JOIN [versions] AS [minV] ON [minV].[rowid] = [dep].[min_version] // WHERE [dep].[manifest] = ? builder.Select() .Column(QCol(depTableAlias, s_DependenciesTable_PackageId_Column_Name)) + .Column(QCol(depTableAlias, s_DependenciesTable_MinVersion_Column_Name)) .Column(QCol(minVersionAlias, VersionTable::ValueName())) .From({ s_DependenciesTable_Table_Name }).As(depTableAlias) - .Join({ VersionTable::TableName() }).As(minVersionAlias) + .LeftOuterJoin({ VersionTable::TableName() }).As(minVersionAlias) .On(QCol(minVersionAlias, SQLite::RowIDName), QCol(depTableAlias, s_DependenciesTable_MinVersion_Column_Name)) .Where(QCol(depTableAlias, s_DependenciesTable_Manifest_Column_Name)).Equals(Unbound); @@ -385,9 +393,10 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_4 Utility::NormalizedString version = ""; if (!select.GetColumnIsNull(1)) { - version = select.GetColumn<std::string>(1); + // If min_version is not NULL, use the corresponding value from Version table. + version = select.GetColumn<std::string>(2); } - resultSet.emplace(std::make_pair(select.GetColumn<SQLite::rowid_t>(0), version)); + resultSet.emplace(std::make_pair(select.GetColumn<SQLite::rowid_t>(0), std::move(version))); } return resultSet; diff --git a/src/AppInstallerRepositoryCore/PackageDependenciesValidation.cpp b/src/AppInstallerRepositoryCore/PackageDependenciesValidation.cpp @@ -110,47 +110,51 @@ namespace AppInstaller::Repository std::vector<ValidationError> dependenciesError; bool foundErrors = false; - DependencyGraph graph(rootId, [&](const Dependency& node) { - - DependencyList depList; - if (node.Id == rootId.Id) - { - return GetDependencies(manifest, DependencyType::Package); - } - - auto packageLatest = GetPackageLatestVersion(index, node.Id); - if (!packageLatest.has_value()) + DependencyGraph graph + { + rootId, + [&](const Dependency& node) { - std::string error = ManifestError::MissingManifestDependenciesNode; - error.append(" ").append(node.Id); - dependenciesError.emplace_back(ValidationError(error)); - foundErrors = true; - return depList; - } + DependencyList depList; + if (node.Id == rootId.Id) + { + return GetDependencies(manifest, DependencyType::Package); + } - if (node.MinVersion > packageLatest.value().second) - { - std::string error = ManifestError::NoSuitableMinVersion; - error.append(" ").append(node.Id); - dependenciesError.emplace_back(ValidationError(error)); - foundErrors = true; - return depList; - } + auto packageLatest = GetPackageLatestVersion(index, node.Id); + if (!packageLatest.has_value()) + { + std::string error = ManifestError::MissingManifestDependenciesNode; + error.append(" ").append(node.Id); + dependenciesError.emplace_back(ValidationError(error)); + foundErrors = true; + return depList; + } - auto packageLatestDependencies = index->GetDependenciesByManifestRowId(packageLatest.value().first); - std::for_each( - packageLatestDependencies.begin(), - packageLatestDependencies.end(), - [&](std::pair<SQLite::rowid_t, Utility::NormalizedString> row) + if (node.MinVersion > packageLatest.value().second) { - auto manifestRowId = index->GetManifestIdByKey(row.first, "", ""); - auto packageId = index->GetPropertyByManifestId(manifestRowId.value(), PackageVersionProperty::Id); - Dependency dep(DependencyType::Package, packageId.value(), row.second); - depList.Add(dep); - }); + std::string error = ManifestError::NoSuitableMinVersion; + error.append(" ").append(node.Id); + dependenciesError.emplace_back(ValidationError(error)); + foundErrors = true; + return depList; + } - return depList; - }); + auto packageLatestDependencies = index->GetDependenciesByManifestRowId(packageLatest.value().first); + std::for_each( + packageLatestDependencies.begin(), + packageLatestDependencies.end(), + [&](std::pair<SQLite::rowid_t, Utility::NormalizedString> row) + { + auto manifestRowId = index->GetManifestIdByKey(row.first, "", ""); + auto packageId = index->GetPropertyByManifestId(manifestRowId.value(), PackageVersionProperty::Id); + Dependency dep(DependencyType::Package, packageId.value(), row.second); + depList.Add(dep); + }); + + return depList; + } + }; graph.BuildGraph(); @@ -228,7 +232,7 @@ namespace AppInstaller::Repository } ); - if (breakingManifests.size()) + if (!breakingManifests.empty()) { ThrowOnManifestValidationFailed( breakingManifests, Manifest::ManifestError::MultiManifestPackageHasDependencies);