winget-cli

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

commit 384789e3619b96b740eb0e55c101d9cda0e7b4bb
parent aec31b87bbacc37693a270f1e002f4ceac3ca73c
Author: JohnMcPMS <johnmcp@microsoft.com>
Date:   Thu, 27 Feb 2020 18:57:07 -0800

Move to using {id,version,channel} as the key for update and remove, rather than path (#44)


Diffstat:
Msrc/AppInstallerCLITests/SQLiteIndex.cpp | 81+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/AppInstallerRepositoryCore/Microsoft/SQLiteIndex.h | 10++++++----
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/Interface.cpp | 86++++++++++++++++++++++++++++++++++++++-----------------------------------------
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ManifestTable.cpp | 32++++++++++++++++++++++++++++++--
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ManifestTable.h | 12++++++++----
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToOneTable.cpp | 31+++++++++++++++++++++----------
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToOneTable.h | 10++++++++++
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/PathPartTable.h | 3+++
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/ISQLiteIndex.h | 6++++--
Msrc/AppInstallerSQLiteIndexUtil/AppInstallerSQLiteIndexUtil.h | 7++++---
10 files changed, 208 insertions(+), 70 deletions(-)

diff --git a/src/AppInstallerCLITests/SQLiteIndex.cpp b/src/AppInstallerCLITests/SQLiteIndex.cpp @@ -301,6 +301,87 @@ TEST_CASE("SQLiteIndex_UpdateManifest", "[sqliteindex][V1_0]") REQUIRE(Schema::V1_0::CommandsTable::IsEmpty(connection)); } +TEST_CASE("SQLiteIndex_UpdateManifestChangePath", "[sqliteindex][V1_0]") +{ + TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; + INFO("Using temporary file named: " << tempFile.GetPath()); + + std::string manifestPath = "test/id/test.id-1.0.0.yml"; + Manifest manifest; + manifest.Id = "test.id"; + manifest.Name = "Test Name"; + manifest.AppMoniker = "testmoniker"; + manifest.Version = "1.0.0"; + manifest.Channel = "test"; + manifest.Tags = { "t1", "t2" }; + manifest.Commands = { "test1", "test2" }; + + { + SQLiteIndex index = SQLiteIndex::CreateNew(tempFile, { 1, 0 }); + + index.AddManifest(manifest, manifestPath); + } + + { + // Open it directly to directly test table state + Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); + + REQUIRE(!Schema::V1_0::ManifestTable::IsEmpty(connection)); + REQUIRE(!Schema::V1_0::IdTable::IsEmpty(connection)); + REQUIRE(!Schema::V1_0::NameTable::IsEmpty(connection)); + REQUIRE(!Schema::V1_0::MonikerTable::IsEmpty(connection)); + REQUIRE(!Schema::V1_0::VersionTable::IsEmpty(connection)); + REQUIRE(!Schema::V1_0::ChannelTable::IsEmpty(connection)); + REQUIRE(!Schema::V1_0::PathPartTable::IsEmpty(connection)); + REQUIRE(!Schema::V1_0::TagsTable::IsEmpty(connection)); + REQUIRE(!Schema::V1_0::CommandsTable::IsEmpty(connection)); + } + + { + SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteIndex::OpenDisposition::ReadWrite); + + manifestPath = "test/newid/test.newid-1.0.0.yml"; + + // Update with path update should indicate change + REQUIRE(index.UpdateManifest(manifest, manifestPath)); + } + + { + // Open it directly to directly test table state + Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); + + REQUIRE(!Schema::V1_0::ManifestTable::IsEmpty(connection)); + REQUIRE(!Schema::V1_0::IdTable::IsEmpty(connection)); + REQUIRE(!Schema::V1_0::NameTable::IsEmpty(connection)); + REQUIRE(!Schema::V1_0::MonikerTable::IsEmpty(connection)); + REQUIRE(!Schema::V1_0::VersionTable::IsEmpty(connection)); + REQUIRE(!Schema::V1_0::ChannelTable::IsEmpty(connection)); + REQUIRE(!Schema::V1_0::PathPartTable::IsEmpty(connection)); + REQUIRE(!Schema::V1_0::TagsTable::IsEmpty(connection)); + REQUIRE(!Schema::V1_0::CommandsTable::IsEmpty(connection)); + } + + { + SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteIndex::OpenDisposition::ReadWrite); + + // Now remove manifest, with unknown path + index.RemoveManifest(manifest, ""); + } + + // Open it directly to directly test table state + Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); + + REQUIRE(Schema::V1_0::ManifestTable::IsEmpty(connection)); + REQUIRE(Schema::V1_0::IdTable::IsEmpty(connection)); + REQUIRE(Schema::V1_0::NameTable::IsEmpty(connection)); + REQUIRE(Schema::V1_0::MonikerTable::IsEmpty(connection)); + REQUIRE(Schema::V1_0::VersionTable::IsEmpty(connection)); + REQUIRE(Schema::V1_0::ChannelTable::IsEmpty(connection)); + REQUIRE(Schema::V1_0::PathPartTable::IsEmpty(connection)); + REQUIRE(Schema::V1_0::TagsTable::IsEmpty(connection)); + REQUIRE(Schema::V1_0::CommandsTable::IsEmpty(connection)); +} + TEST_CASE("PathPartTable_EnsurePathExists_Negative_Paths", "[sqliteindex][V1_0]") { // Open it directly to directly test pathpart table diff --git a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndex.h b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndex.h @@ -53,18 +53,20 @@ namespace AppInstaller::Repository::Microsoft // If the function succeeds, the manifest has been added. void AddManifest(const Manifest::Manifest& manifest, const std::filesystem::path& relativePath); - // Updates the manifest at the repository relative path in the index. + // Updates the manifest with matching { Id, Version, Channel } in the index. // The return value indicates whether the index was modified by the function. bool UpdateManifest(const std::filesystem::path& manifestPath, const std::filesystem::path& relativePath); - // Updates the manifest at the repository relative path in the index. + // Updates the manifest with matching { Id, Version, Channel } in the index. // The return value indicates whether the index was modified by the function. bool UpdateManifest(const Manifest::Manifest& manifest, const std::filesystem::path& relativePath); - // Removes the manifest at the repository relative path from the index. + // Removes the manifest with matching { Id, Version, Channel } from the index. + // Path is currently ignored. void RemoveManifest(const std::filesystem::path& manifestPath, const std::filesystem::path& relativePath); - // Removes the manifest at the repository relative path from the index. + // Removes the manifest with matching { Id, Version, Channel } from the index. + // Path is currently ignored. void RemoveManifest(const Manifest::Manifest& manifest, const std::filesystem::path& relativePath); // Removes data that is no longer needed for an index that is to be published. diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/Interface.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/Interface.cpp @@ -20,57 +20,36 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 { namespace { - // Holds the information on a potentially existing manifest. - struct ExistingManifestInfo - { - SQLite::rowid_t PathLeaf; - std::optional<SQLite::rowid_t> Manifest; - }; - // Gets an existing manifest by its rowid. // The return value contains the path leaf and manifest rowid, if they exist. - ExistingManifestInfo GetExistingManifestId(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::filesystem::path& relativePath) + std::optional<SQLite::rowid_t> GetExistingManifestId(SQLite::Connection& connection, const Manifest::Manifest& manifest) { - auto [pathFound, pathLeafId] = PathPartTable::EnsurePathExists(connection, relativePath, false); - - // If we do not find the path, there can be no manifest - if (!pathFound) + std::optional<SQLite::rowid_t> idId = IdTable::SelectIdByValue(connection, manifest.Id); + if (!idId) { - AICLI_LOG(Repo, Info, << "Did not find a manifest to remove at path: " << relativePath.u8string()); + AICLI_LOG(Repo, Info, << "Did not find an Id { " << manifest.Id << " }"); return {}; } - ExistingManifestInfo result{}; - result.PathLeaf = pathLeafId; - result.Manifest = ManifestTable::SelectByValueId<PathPartTable>(connection, result.PathLeaf); - - // If the manifest didn't actually exist, then remove the path - if (!result.Manifest) + std::optional<SQLite::rowid_t> versionId = VersionTable::SelectIdByValue(connection, manifest.Version); + if (!versionId) { - AICLI_LOG(Repo, Info, << "Did not find a manifest row for the path: " << relativePath.u8string()); - PathPartTable::RemovePathById(connection, result.PathLeaf); + AICLI_LOG(Repo, Info, << "Did not find a Version { " << manifest.Version << " }"); return {}; } - // Ensure that the given manifest matches the data in the index - auto [idValue, versionValue, channelValue] = ManifestTable::GetValuesById<IdTable, VersionTable, ChannelTable>(connection, result.Manifest.value()); - - if (idValue != manifest.Id) + std::optional<SQLite::rowid_t> channelId = ChannelTable::SelectIdByValue(connection, manifest.Channel); + if (!channelId) { - AICLI_LOG(Repo, Error, << "Existing manifest in index does not match given value for Id: [" << idValue << "] != [" << manifest.Id << "]"); - THROW_HR(E_NOT_VALID_STATE); + AICLI_LOG(Repo, Info, << "Did not find a Channel { " << manifest.Channel << " }"); + return {}; } - if (versionValue != manifest.Version) - { - AICLI_LOG(Repo, Error, << "Existing manifest in index does not match given value for Version: [" << versionValue << "] != [" << manifest.Version << "]"); - THROW_HR(E_NOT_VALID_STATE); - } + auto result = ManifestTable::SelectByValueIds<IdTable, VersionTable, ChannelTable>(connection, { idId.value(), versionId.value(), channelId.value() }); - if (channelValue != manifest.Channel) + if (!result) { - AICLI_LOG(Repo, Error, << "Existing manifest in index does not match given value for Channel: [" << channelValue << "] != [" << manifest.Channel << "]"); - THROW_HR(E_NOT_VALID_STATE); + AICLI_LOG(Repo, Info, << "Did not find a manifest row for { " << manifest.Id << ", " << manifest.Version << ", " << manifest.Channel << " }"); } return result; @@ -157,12 +136,12 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 bool Interface::UpdateManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::filesystem::path& relativePath) { - ExistingManifestInfo manifestInfo = GetExistingManifestId(connection, manifest, relativePath); + auto manifestResult = GetExistingManifestId(connection, manifest); // If the manifest doesn't actually exist, fail the update. - THROW_HR_IF(E_NOT_SET, !manifestInfo.Manifest); + THROW_HR_IF(E_NOT_SET, !manifestResult); - SQLite::rowid_t manifestId = manifestInfo.Manifest.value(); + SQLite::rowid_t manifestId = manifestResult.value(); auto [idInIndex, nameInIndex, monikerInIndex, versionInIndex, channelInIndex] = ManifestTable::GetValuesById<IdTable, NameTable, MonikerTable, VersionTable, ChannelTable>(connection, manifestId); @@ -189,6 +168,23 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 indexModified = true; } + // Update path table if necessary + auto [existingPathLeafId] = ManifestTable::GetIdsById<PathPartTable>(connection, manifestId); + auto [pathAdded, newPathLeafId] = PathPartTable::EnsurePathExists(connection, relativePath, true); + + if (pathAdded) + { + // Path was added, so we need to update the manifest table and delete the old path + ManifestTable::UpdateValueIdById<PathPartTable>(connection, manifestId, newPathLeafId); + PathPartTable::RemovePathById(connection, existingPathLeafId); + indexModified = true; + } + else + { + // The path already existed, so it must either match the existing manifest path or it is an error + THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS), existingPathLeafId != newPathLeafId); + } + // Update all 1:N tables as necessary indexModified = TagsTable::UpdateIfNeededByManifestId(connection, manifest.Tags, manifestId) || indexModified; indexModified = CommandsTable::UpdateIfNeededByManifestId(connection, manifest.Commands, manifestId) || indexModified; @@ -198,18 +194,18 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 return indexModified; } - void Interface::RemoveManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::filesystem::path& relativePath) + void Interface::RemoveManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::filesystem::path&) { - ExistingManifestInfo manifestInfo = GetExistingManifestId(connection, manifest, relativePath); + auto manifestResult = GetExistingManifestId(connection, manifest); // If the manifest doesn't actually exist, fail the remove. - THROW_HR_IF(E_NOT_SET, !manifestInfo.Manifest); + THROW_HR_IF(E_NOT_SET, !manifestResult); - SQLite::rowid_t manifestId = manifestInfo.Manifest.value(); + SQLite::rowid_t manifestId = manifestResult.value(); // Get the ids of the values from the manifest table - auto [idId, nameId, monikerId, versionId, channelId] = - ManifestTable::GetIdsById<IdTable, NameTable, MonikerTable, VersionTable, ChannelTable>(connection, manifestId); + auto [idId, nameId, monikerId, versionId, channelId, pathLeafId] = + ManifestTable::GetIdsById<IdTable, NameTable, MonikerTable, VersionTable, ChannelTable, PathPartTable>(connection, manifestId); SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "removemanifest_v1_0"); @@ -224,7 +220,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 ChannelTable::DeleteIfNotNeededById(connection, channelId); // Remove the path - PathPartTable::RemovePathById(connection, manifestInfo.PathLeaf); + PathPartTable::RemovePathById(connection, pathLeafId); // Remove all of the 1:N data that is no longer referenced. TagsTable::DeleteIfNotNeededByManifestId(connection, manifestId); diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ManifestTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ManifestTable.cpp @@ -14,13 +14,41 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 namespace details { - std::optional<SQLite::rowid_t> ManifestTableSelectByValueId(SQLite::Connection& connection, std::string_view valueName, SQLite::rowid_t id) + std::optional<SQLite::rowid_t> ManifestTableSelectByValueIds( + SQLite::Connection& connection, + std::initializer_list<std::string_view> values, + std::initializer_list<SQLite::rowid_t> ids) { + THROW_HR_IF(E_INVALIDARG, values.size() != ids.size()); + SQLite::Builder::StatementBuilder builder; - builder.Select(SQLite::RowIDName).From(s_ManifestTable_Table_Name).Where(valueName).Equals(id).Limit(1); + builder.Select(SQLite::RowIDName).From(s_ManifestTable_Table_Name); + + bool isFirst = true; + + for (const auto& value : values) + { + if (isFirst) + { + builder.Where(value).Equals(SQLite::Builder::Unbound); + isFirst = false; + } + else + { + builder.And(value).Equals(SQLite::Builder::Unbound); + } + } + + builder.Limit(1); SQLite::Statement select = builder.Prepare(connection); + int bindIndex = 0; + for (const auto& id : ids) + { + select.Bind(++bindIndex, id); + } + if (select.Step()) { return select.GetColumn<SQLite::rowid_t>(0); diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ManifestTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ManifestTable.h @@ -13,7 +13,10 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 namespace details { // Selects a manifest by the given value id. - std::optional<SQLite::rowid_t> ManifestTableSelectByValueId(SQLite::Connection& connection, std::string_view valueName, SQLite::rowid_t id); + std::optional<SQLite::rowid_t> ManifestTableSelectByValueIds( + SQLite::Connection& connection, + std::initializer_list<std::string_view> values, + std::initializer_list<SQLite::rowid_t> ids); // Gets the requested ids for the manifest with the given rowid. SQLite::Statement ManifestTableGetIdsById_Statement( @@ -56,10 +59,11 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 static SQLite::rowid_t Insert(SQLite::Connection& connection, std::initializer_list<ManifestOneToOneValue> values); // Select the first rowid of the manifest with the given value. - template <typename Table> - static std::optional<SQLite::rowid_t> SelectByValueId(SQLite::Connection& connection, SQLite::rowid_t id) + template <typename... Tables> + static std::optional<SQLite::rowid_t> SelectByValueIds(SQLite::Connection& connection, std::initializer_list<SQLite::rowid_t> ids) { - return details::ManifestTableSelectByValueId(connection, Table::ValueName(), id); + static_assert(sizeof...(Tables) >= 1); + return details::ManifestTableSelectByValueIds(connection, { Tables::ValueName()... }, ids); } // Gets the ids requested for the manifest with the given rowid. diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToOneTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToOneTable.cpp @@ -22,18 +22,29 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 createTableBuilder.Execute(connection); } - SQLite::rowid_t OneToOneTableEnsureExists(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, std::string_view value) + std::optional<SQLite::rowid_t> OneToOneTableSelectIdByValue(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, std::string_view value) { - { - SQLite::Builder::StatementBuilder selectBuilder; - selectBuilder.Select(SQLite::RowIDName).From(tableName).Where(valueName).Equals(value); + SQLite::Builder::StatementBuilder selectBuilder; + selectBuilder.Select(SQLite::RowIDName).From(tableName).Where(valueName).Equals(value); - SQLite::Statement select = selectBuilder.Prepare(connection); + SQLite::Statement select = selectBuilder.Prepare(connection); - if (select.Step()) - { - return select.GetColumn<SQLite::rowid_t>(0); - } + if (select.Step()) + { + return select.GetColumn<SQLite::rowid_t>(0); + } + else + { + return {}; + } + } + + SQLite::rowid_t OneToOneTableEnsureExists(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, std::string_view value) + { + auto selectResult = OneToOneTableSelectIdByValue(connection, tableName, valueName, value); + if (selectResult) + { + return selectResult.value(); } SQLite::Builder::StatementBuilder insertBuilder; @@ -47,7 +58,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 void OneToOneTableDeleteIfNotNeededById(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, SQLite::rowid_t id) { // If a manifest is found that references this id, then we are done. - if (ManifestTableSelectByValueId(connection, valueName, id)) + if (ManifestTableSelectByValueIds(connection, { valueName }, { id })) { return; } diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToOneTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToOneTable.h @@ -2,6 +2,7 @@ // Licensed under the MIT License. #pragma once #include "SQLiteWrapper.h" +#include <optional> #include <string> #include <string_view> @@ -13,6 +14,9 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 // Creates the table. void CreateOneToOneTable(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName); + // Selects the value from the table, returning the rowid if it exists. + std::optional<SQLite::rowid_t> OneToOneTableSelectIdByValue(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, std::string_view value); + // Ensures that the values exists in the table. SQLite::rowid_t OneToOneTableEnsureExists(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, std::string_view value); @@ -51,6 +55,12 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 return TableInfo::ValueName(); } + // Selects the value from the table, returning the rowid if it exists. + static std::optional<SQLite::rowid_t> SelectIdByValue(SQLite::Connection& connection, std::string_view value) + { + return details::OneToOneTableSelectIdByValue(connection, TableInfo::TableName(), TableInfo::ValueName(), value); + } + // Ensures that the given value exists in the table, returning the rowid. static SQLite::rowid_t EnsureExists(SQLite::Connection& connection, std::string_view value) { diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/PathPartTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/PathPartTable.h @@ -12,6 +12,9 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 // A table that represents a single manifest struct PathPartTable { + // The id type + using id_t = SQLite::rowid_t; + // Creates the table. static void Create(SQLite::Connection& connection); diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/ISQLiteIndex.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/ISQLiteIndex.h @@ -29,10 +29,12 @@ namespace AppInstaller::Repository::Microsoft::Schema // Adds the manifest at the repository relative path to the index. virtual void AddManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::filesystem::path& relativePath) = 0; - // Updates the manifest at the repository relative path in the index. + // Updates the manifest with matching { Id, Version, Channel } in the index. + // The return value indicates whether the index was modified by the function. virtual bool UpdateManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::filesystem::path& relativePath) = 0; - // Removes the manifest at the repository relative path from the index. + // Removes the manifest with matching { Id, Version, Channel } from the index. + // Path is currently ignored. virtual void RemoveManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::filesystem::path& relativePath) = 0; // Removes data that is no longer needed for an index that is to be published. diff --git a/src/AppInstallerSQLiteIndexUtil/AppInstallerSQLiteIndexUtil.h b/src/AppInstallerSQLiteIndexUtil/AppInstallerSQLiteIndexUtil.h @@ -46,15 +46,16 @@ extern "C" APPINSTALLER_SQLITE_INDEX_STRING manifestPath, APPINSTALLER_SQLITE_INDEX_STRING relativePath); - // Updates the manifest at the repository relative path in the index. - // The out value indicates whether the index was modified by the function. + // Updates the manifest with matching { Id, Version, Channel } in the index. + // The return value indicates whether the index was modified by the function. APPINSTALLER_SQLITE_INDEX_API AppInstallerSQLiteIndexUpdateManifest( APPINSTALLER_SQLITE_INDEX_HANDLE index, APPINSTALLER_SQLITE_INDEX_STRING manifestPath, APPINSTALLER_SQLITE_INDEX_STRING relativePath, bool* indexModified); - // Removes the manifest at the repository relative path from the index. + // Removes the manifest with matching { Id, Version, Channel } from the index. + // Path is currently ignored. APPINSTALLER_SQLITE_INDEX_API AppInstallerSQLiteIndexRemoveManifest( APPINSTALLER_SQLITE_INDEX_HANDLE index, APPINSTALLER_SQLITE_INDEX_STRING manifestPath,