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:
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,