winget-cli

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

commit e1ff71f57dfbb5f27bfae82b18cefea203f0d2e6
parent f4f312a6abe801eba5f5d7e95ac6eafca722991e
Author: JohnMcPMS <johnmcp@microsoft.com>
Date:   Fri,  9 Oct 2020 11:00:43 -0700

Create integer primary keys in tables that need to be referenced (all but maps) (#604)


Diffstat:
Msrc/AppInstallerCLITests/SQLiteIndex.cpp | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ManifestTable.cpp | 3+++
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToOneTable.cpp | 1+
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/PathPartTable.cpp | 1+
Msrc/AppInstallerRepositoryCore/SQLiteStatementBuilder.cpp | 14++++++++++++++
Msrc/AppInstallerRepositoryCore/SQLiteStatementBuilder.h | 16++++++++++++++++
6 files changed, 97 insertions(+), 0 deletions(-)

diff --git a/src/AppInstallerCLITests/SQLiteIndex.cpp b/src/AppInstallerCLITests/SQLiteIndex.cpp @@ -376,6 +376,68 @@ TEST_CASE("SQLiteIndex_RemoveManifest", "[sqliteindex][V1_0]") REQUIRE(Schema::V1_0::CommandsTable::IsEmpty(connection)); } +TEST_CASE("SQLiteIndex_RemoveManifest_EnsureConsistentRowId", "[sqliteindex]") +{ + TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; + INFO("Using temporary file named: " << tempFile.GetPath()); + + std::string manifest1Path = "test/id/test.id-1.0.0.yaml"; + Manifest manifest1; + manifest1.Id = "test.id"; + manifest1.Name = "Test Name"; + manifest1.AppMoniker = "testmoniker"; + manifest1.Version = "1.0.0"; + manifest1.Channel = "test"; + manifest1.Tags = { "t1", "t2" }; + manifest1.Commands = { "test1", "test2" }; + + std::string manifest2Path = "test/woah/test.id-1.0.0.yaml"; + Manifest manifest2; + manifest2.Id = "test.woah"; + manifest2.Name = "Test Name WOAH"; + manifest2.AppMoniker = "testmoniker"; + manifest2.Version = "1.0.0"; + manifest2.Channel = "test"; + manifest2.Tags = {}; + manifest2.Commands = { "test1", "test2", "test3" }; + + SQLiteIndex index = CreateTestIndex(tempFile); + + index.AddManifest(manifest1, manifest1Path); + index.AddManifest(manifest2, manifest2Path); + + // Get the second manifest's id for validating consistency + SearchRequest request; + request.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, manifest2.Id)); + auto result = index.Search(request); + + REQUIRE(result.Matches.size() == 1); + auto manifest2IdRowId = result.Matches[0].first; + + auto rowId = index.GetManifestIdByKey(manifest2IdRowId, {}, {}); + REQUIRE(rowId); + auto manifest2RowId = rowId.value(); + + // Now remove manifest1 and prepare + index.RemoveManifest(manifest1, manifest1Path); + index.PrepareForPackaging(); + + // Repeat search to ensure consistent ids + result = index.Search(request); + REQUIRE(result.Matches.size() == 1); + REQUIRE(result.Matches[0].first == manifest2IdRowId); + + rowId = index.GetManifestIdByKey(manifest2IdRowId, {}, {}); + REQUIRE(rowId); + REQUIRE(rowId.value() == manifest2RowId); + + REQUIRE(manifest2.Id == index.GetPropertyByManifestId(manifest2RowId, PackageVersionProperty::Id)); + REQUIRE(manifest2.Name == index.GetPropertyByManifestId(manifest2RowId, PackageVersionProperty::Name)); + REQUIRE(manifest2.Version == index.GetPropertyByManifestId(manifest2RowId, PackageVersionProperty::Version)); + REQUIRE(manifest2.Channel == index.GetPropertyByManifestId(manifest2RowId, PackageVersionProperty::Channel)); + REQUIRE(manifest2Path == index.GetPropertyByManifestId(manifest2RowId, PackageVersionProperty::RelativePath)); +} + TEST_CASE("SQLiteIndex_RemoveManifestFile", "[sqliteindex][V1_0]") { TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ManifestTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ManifestTable.cpp @@ -240,6 +240,9 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 StatementBuilder createTableBuilder; createTableBuilder.CreateTable(s_ManifestTable_Table_Name).BeginColumns(); + // Add an integer primary key to keep the manifest rowid consistent + createTableBuilder.Column(IntegerPrimaryKey()); + for (const ManifestColumnInfo& value : values) { createTableBuilder.Column(ColumnBuilder(value.Name, Type::Int64).NotNull()); diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToOneTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToOneTable.cpp @@ -26,6 +26,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 StatementBuilder createTableBuilder; createTableBuilder.CreateTable(tableName).Columns({ + IntegerPrimaryKey(), ColumnBuilder(valueName, Type::Text).NotNull() }); diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/PathPartTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/PathPartTable.cpp @@ -104,6 +104,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 StatementBuilder createTableBuilder; createTableBuilder.CreateTable(s_PathPartTable_Table_Name).Columns({ + IntegerPrimaryKey(), ColumnBuilder(s_PathPartTable_ParentValue_Name, Type::Int64), ColumnBuilder(s_PathPartTable_PartValue_Name, Type::Text).NotNull() }); diff --git a/src/AppInstallerRepositoryCore/SQLiteStatementBuilder.cpp b/src/AppInstallerRepositoryCore/SQLiteStatementBuilder.cpp @@ -143,6 +143,20 @@ namespace AppInstaller::Repository::SQLite::Builder } } + IntegerPrimaryKey::IntegerPrimaryKey() + { + m_stream << SQLite::RowIDName << " INTEGER PRIMARY KEY"; + } + + IntegerPrimaryKey& IntegerPrimaryKey::AutoIncrement(bool isTrue) + { + if (isTrue) + { + m_stream << " AUTOINCREMENT"; + } + return *this; + } + ColumnBuilder::ColumnBuilder(std::string_view column, Type type) { OutputColumns(m_stream, "", column); diff --git a/src/AppInstallerRepositoryCore/SQLiteStatementBuilder.h b/src/AppInstallerRepositoryCore/SQLiteStatementBuilder.h @@ -97,6 +97,22 @@ namespace AppInstaller::Repository::SQLite::Builder Min }; + // Helper to mark create an integer primary key for rowid, making it stable across vacuum. + struct IntegerPrimaryKey : public details::SubBuilderBase + { + IntegerPrimaryKey(); + + IntegerPrimaryKey(const IntegerPrimaryKey&) = default; + IntegerPrimaryKey& operator=(const IntegerPrimaryKey&) = default; + + IntegerPrimaryKey(IntegerPrimaryKey&&) noexcept = default; + IntegerPrimaryKey& operator=(IntegerPrimaryKey&&) noexcept = default; + + // Set the column to autoincrement. SQLite recommends against using this value unless + // you need to ensure that rowids are not ever reused. + IntegerPrimaryKey& AutoIncrement(bool isTrue = true); + }; + // Helper used when creating a table. struct ColumnBuilder : public details::SubBuilderBase {