winget-cli

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

commit e781da4da6ddf80d670d5a73f2f601e46a7e5507
parent 68af39b2256872ea30fdb695aa9b711633f63c3b
Author: JohnMcPMS <johnmcp@microsoft.com>
Date:   Thu, 19 Oct 2023 09:49:51 -0700

Move SQLite base code (#3790)

This change moves the core SQLite code to the shared lib.  It changes the namespace of all of the moved files, but the goal is absolutely zero functionality difference for now.
Diffstat:
Msrc/AppInstallerCLICore/CheckpointManager.cpp | 2+-
Msrc/AppInstallerCLICore/PortableInstaller.cpp | 2+-
Msrc/AppInstallerCLICore/Workflows/PinFlow.cpp | 4++--
Msrc/AppInstallerCLITests/CheckpointDatabase.cpp | 8++++----
Msrc/AppInstallerCLITests/PackageTrackingCatalog.cpp | 4++--
Msrc/AppInstallerCLITests/PinFlow.cpp | 1+
Msrc/AppInstallerCLITests/PinningIndex.cpp | 12++++++------
Msrc/AppInstallerCLITests/PortableIndex.cpp | 16++++++++--------
Msrc/AppInstallerCLITests/PortableInstaller.cpp | 4++--
Msrc/AppInstallerCLITests/SQLiteIndex.cpp | 225++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/AppInstallerCLITests/SQLiteIndexSource.cpp | 4++--
Msrc/AppInstallerCLITests/SQLiteWrapper.cpp | 6+++---
Msrc/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj | 2--
Msrc/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj.filters | 6------
Msrc/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj | 27---------------------------
Msrc/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters | 57++++++---------------------------------------------------
Msrc/AppInstallerRepositoryCore/Microsoft/CheckpointDatabase.cpp | 6+++---
Msrc/AppInstallerRepositoryCore/Microsoft/CheckpointDatabase.h | 10+++++-----
Msrc/AppInstallerRepositoryCore/Microsoft/PinningIndex.cpp | 8++++----
Msrc/AppInstallerRepositoryCore/Microsoft/PinningIndex.h | 10+++++-----
Msrc/AppInstallerRepositoryCore/Microsoft/PortableIndex.cpp | 8++++----
Msrc/AppInstallerRepositoryCore/Microsoft/PortableIndex.h | 10+++++-----
Msrc/AppInstallerRepositoryCore/Microsoft/PredefinedInstalledSourceFactory.cpp | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/PredefinedWriteableSourceFactory.cpp | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/SQLiteIndex.cpp | 14+++++++-------
Msrc/AppInstallerRepositoryCore/Microsoft/SQLiteIndex.h | 18+++++++++---------
Dsrc/AppInstallerRepositoryCore/Microsoft/SQLiteStorageBase.cpp | 115-------------------------------------------------------------------------------
Dsrc/AppInstallerRepositoryCore/Microsoft/SQLiteStorageBase.h | 49-------------------------------------------------
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/Interface.h | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/Interface_1_0.cpp | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ManifestTable.cpp | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ManifestTable.h | 4++--
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToManyTable.cpp | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToManyTable.h | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToOneTable.cpp | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToOneTable.h | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/PathPartTable.cpp | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/PathPartTable.h | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/SearchResultsTable.h | 4++--
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/SearchResultsTable_1_0.cpp | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_1/Interface.h | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_1/Interface_1_1.cpp | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_1/ManifestMetadataTable.cpp | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_1/ManifestMetadataTable.h | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_2/Interface.h | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_2/Interface_1_2.cpp | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_3/HashVirtualTable.h | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_3/Interface.h | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_3/Interface_1_3.cpp | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_4/DependenciesTable.cpp | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_4/DependenciesTable.h | 4++--
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_4/Interface.h | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_4/Interface_1_4.cpp | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_5/ArpVersionVirtualTable.h | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_5/Interface.h | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_5/Interface_1_5.cpp | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_6/Interface.h | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_6/Interface_1_6.cpp | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_7/Interface.h | 6+++++-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_7/Interface_1_7.cpp | 8++++----
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDataTable.cpp | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDataTable.h | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDatabaseInterface.h | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDatabaseInterface_1_0.cpp | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointDatabase.h | 6+++---
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/IPinningIndex.h | 6+++---
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/IPortableIndex.h | 6+++---
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/ISQLiteIndex.h | 9+++------
Dsrc/AppInstallerRepositoryCore/Microsoft/Schema/MetadataTable.cpp | 61-------------------------------------------------------------
Dsrc/AppInstallerRepositoryCore/Microsoft/Schema/MetadataTable.h | 72------------------------------------------------------------------------
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/Pinning_1_0/PinTable.cpp | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/Pinning_1_0/PinTable.h | 4++--
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/Pinning_1_0/PinningIndexInterface.h | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/Pinning_1_0/PinningIndexInterface_1_0.cpp | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/Portable_1_0/PortableIndexInterface.h | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/Portable_1_0/PortableIndexInterface_1_0.cpp | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/Portable_1_0/PortableTable.cpp | 2+-
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/Portable_1_0/PortableTable.h | 4++--
Dsrc/AppInstallerRepositoryCore/Microsoft/Schema/Version.cpp | 64----------------------------------------------------------------
Dsrc/AppInstallerRepositoryCore/Microsoft/Schema/Version.h | 57---------------------------------------------------------
Msrc/AppInstallerRepositoryCore/PackageTrackingCatalog.cpp | 2+-
Msrc/AppInstallerRepositoryCore/Rest/Schema/IRestClient.h | 2+-
Dsrc/AppInstallerRepositoryCore/SQLiteStatementBuilder.cpp | 944-------------------------------------------------------------------------------
Dsrc/AppInstallerRepositoryCore/SQLiteStatementBuilder.h | 499-------------------------------------------------------------------------------
Dsrc/AppInstallerRepositoryCore/SQLiteTempTable.cpp | 42------------------------------------------
Dsrc/AppInstallerRepositoryCore/SQLiteTempTable.h | 35-----------------------------------
Dsrc/AppInstallerRepositoryCore/SQLiteWrapper.cpp | 374-------------------------------------------------------------------------------
Dsrc/AppInstallerRepositoryCore/SQLiteWrapper.h | 325-------------------------------------------------------------------------------
Msrc/AppInstallerSharedLib/AppInstallerSharedLib.vcxproj | 30++++++++++++++++++++++++++++++
Msrc/AppInstallerSharedLib/AppInstallerSharedLib.vcxproj.filters | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rsrc/AppInstallerRepositoryCore/ICU/SQLiteICU.c -> src/AppInstallerSharedLib/ICU/SQLiteICU.c | 0
Rsrc/AppInstallerRepositoryCore/ICU/SQLiteICU.h -> src/AppInstallerSharedLib/ICU/SQLiteICU.h | 0
Rsrc/AppInstallerCommonCore/ManagedFile.cpp -> src/AppInstallerSharedLib/ManagedFile.cpp | 0
Rsrc/AppInstallerCommonCore/Public/winget/ManagedFile.h -> src/AppInstallerSharedLib/Public/winget/ManagedFile.h | 0
Asrc/AppInstallerSharedLib/Public/winget/SQLiteMetadataTable.h | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/AppInstallerSharedLib/Public/winget/SQLiteStatementBuilder.h | 499+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/AppInstallerSharedLib/Public/winget/SQLiteStorageBase.h | 45+++++++++++++++++++++++++++++++++++++++++++++
Asrc/AppInstallerSharedLib/Public/winget/SQLiteTempTable.h | 35+++++++++++++++++++++++++++++++++++
Asrc/AppInstallerSharedLib/Public/winget/SQLiteVersion.h | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/AppInstallerSharedLib/Public/winget/SQLiteWrapper.h | 325+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/AppInstallerSharedLib/SQLiteMetadataTable.cpp | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/AppInstallerSharedLib/SQLiteStatementBuilder.cpp | 944+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/AppInstallerSharedLib/SQLiteStorageBase.cpp | 116+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/AppInstallerSharedLib/SQLiteTempTable.cpp | 43+++++++++++++++++++++++++++++++++++++++++++
Asrc/AppInstallerSharedLib/SQLiteVersion.cpp | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/AppInstallerSharedLib/SQLiteWrapper.cpp | 375+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/WinGetUtil/Exports.cpp | 2+-
109 files changed, 2985 insertions(+), 2981 deletions(-)

diff --git a/src/AppInstallerCLICore/CheckpointManager.cpp b/src/AppInstallerCLICore/CheckpointManager.cpp @@ -10,7 +10,7 @@ namespace AppInstaller::Checkpoints { using namespace AppInstaller::CLI; using namespace AppInstaller::Repository::Microsoft; - using namespace AppInstaller::Repository::SQLite; + using namespace AppInstaller::SQLite; // This checkpoint name is reserved for the starting checkpoint which captures the automatic metadata. constexpr std::string_view s_AutomaticCheckpoint = "automatic"sv; diff --git a/src/AppInstallerCLICore/PortableInstaller.cpp b/src/AppInstallerCLICore/PortableInstaller.cpp @@ -17,7 +17,7 @@ using namespace AppInstaller::Registry; using namespace AppInstaller::Registry::Portable; using namespace AppInstaller::Registry::Environment; using namespace AppInstaller::Repository; -using namespace AppInstaller::Repository::SQLite; +using namespace AppInstaller::SQLite; using namespace AppInstaller::Repository::Microsoft; using namespace AppInstaller::Repository::Microsoft::Schema; diff --git a/src/AppInstallerCLICore/Workflows/PinFlow.cpp b/src/AppInstallerCLICore/Workflows/PinFlow.cpp @@ -5,11 +5,11 @@ #include "PinFlow.h" #include "TableOutput.h" #include "Microsoft/PinningIndex.h" -#include "Microsoft/SQLiteStorageBase.h" +#include <winget/SQLiteStorageBase.h> #include "winget/RepositorySearch.h" using namespace AppInstaller::Repository; -using namespace AppInstaller::Repository::Microsoft; +using namespace AppInstaller::SQLite; namespace AppInstaller::CLI::Workflow { diff --git a/src/AppInstallerCLITests/CheckpointDatabase.cpp b/src/AppInstallerCLITests/CheckpointDatabase.cpp @@ -8,7 +8,7 @@ using namespace std::string_literals; using namespace TestCommon; using namespace AppInstaller::Repository::Microsoft; -using namespace AppInstaller::Repository::SQLite; +using namespace AppInstaller::SQLite; using namespace AppInstaller::Repository::Microsoft::Schema; using namespace AppInstaller::Checkpoints; @@ -17,11 +17,11 @@ TEST_CASE("CheckpointDatabaseCreateLatestAndReopen", "[checkpointDatabase]") TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); - Schema::Version versionCreated; + Version versionCreated; // Create the database { - std::shared_ptr<CheckpointDatabase> database = CheckpointDatabase::CreateNew(tempFile, Schema::Version::Latest()); + std::shared_ptr<CheckpointDatabase> database = CheckpointDatabase::CreateNew(tempFile, Version::Latest()); versionCreated = database->GetVersion(); } @@ -29,7 +29,7 @@ TEST_CASE("CheckpointDatabaseCreateLatestAndReopen", "[checkpointDatabase]") { INFO("Trying with ReadWrite"); std::shared_ptr<CheckpointDatabase> database = CheckpointDatabase::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); - Schema::Version versionRead = database->GetVersion(); + Version versionRead = database->GetVersion(); REQUIRE(versionRead == versionCreated); } } diff --git a/src/AppInstallerCLITests/PackageTrackingCatalog.cpp b/src/AppInstallerCLITests/PackageTrackingCatalog.cpp @@ -11,14 +11,14 @@ using namespace TestCommon; using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; using namespace AppInstaller::Repository::Microsoft; -using namespace AppInstaller::Repository::SQLite; +using namespace AppInstaller::SQLite; using namespace AppInstaller::Utility; namespace { static Source SimpleTestSetup(const std::string& filePath, SourceDetails& details, Manifest& manifest, std::string& relativePath) { - SQLiteIndex index = SQLiteIndex::CreateNew(filePath, Schema::Version::Latest(), SQLiteIndex::CreateOptions::SupportPathless | SQLiteIndex::CreateOptions::DisableDependenciesSupport); + SQLiteIndex index = SQLiteIndex::CreateNew(filePath, AppInstaller::SQLite::Version::Latest(), SQLiteIndex::CreateOptions::SupportPathless | SQLiteIndex::CreateOptions::DisableDependenciesSupport); TestDataFile testManifest("Manifest-Good.yaml"); manifest = YamlParser::CreateFromPath(testManifest); diff --git a/src/AppInstallerCLITests/PinFlow.cpp b/src/AppInstallerCLITests/PinFlow.cpp @@ -15,6 +15,7 @@ using namespace AppInstaller::CLI::Workflow; using namespace AppInstaller::Repository::Microsoft; using namespace AppInstaller::Utility; using namespace AppInstaller::Pinning; +using namespace AppInstaller::SQLite; void OverrideForOpenPinningIndex(TestContext& context, const std::filesystem::path& indexPath) { diff --git a/src/AppInstallerCLITests/PinningIndex.cpp b/src/AppInstallerCLITests/PinningIndex.cpp @@ -11,7 +11,7 @@ using namespace std::string_literals; using namespace TestCommon; using namespace AppInstaller::Pinning; using namespace AppInstaller::Repository::Microsoft; -using namespace AppInstaller::Repository::SQLite; +using namespace AppInstaller::SQLite; using namespace AppInstaller::Repository::Microsoft::Schema; TEST_CASE("PinningIndexCreateLatestAndReopen", "[pinningIndex]") @@ -19,11 +19,11 @@ TEST_CASE("PinningIndexCreateLatestAndReopen", "[pinningIndex]") TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); - Schema::Version versionCreated; + Version versionCreated; // Create the index { - PinningIndex index = PinningIndex::CreateNew(tempFile, Schema::Version::Latest()); + PinningIndex index = PinningIndex::CreateNew(tempFile, Version::Latest()); versionCreated = index.GetVersion(); } @@ -31,7 +31,7 @@ TEST_CASE("PinningIndexCreateLatestAndReopen", "[pinningIndex]") { INFO("Trying with Read"); PinningIndex index = PinningIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::Read); - Schema::Version versionRead = index.GetVersion(); + Version versionRead = index.GetVersion(); REQUIRE(versionRead == versionCreated); } @@ -39,7 +39,7 @@ TEST_CASE("PinningIndexCreateLatestAndReopen", "[pinningIndex]") { INFO("Trying with ReadWrite"); PinningIndex index = PinningIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); - Schema::Version versionRead = index.GetVersion(); + Version versionRead = index.GetVersion(); REQUIRE(versionRead == versionCreated); } @@ -47,7 +47,7 @@ TEST_CASE("PinningIndexCreateLatestAndReopen", "[pinningIndex]") { INFO("Trying with Immutable"); PinningIndex index = PinningIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::Immutable); - Schema::Version versionRead = index.GetVersion(); + Version versionRead = index.GetVersion(); REQUIRE(versionRead == versionCreated); } } diff --git a/src/AppInstallerCLITests/PortableIndex.cpp b/src/AppInstallerCLITests/PortableIndex.cpp @@ -2,8 +2,8 @@ // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" -#include <SQLiteWrapper.h> -#include <Microsoft/SQLiteStorageBase.h> +#include <winget/SQLiteWrapper.h> +#include <winget/SQLiteStorageBase.h> #include <Microsoft/Schema/IPortableIndex.h> #include <Microsoft/PortableIndex.h> #include <Microsoft/Schema/Portable_1_0/PortableTable.h> @@ -13,7 +13,7 @@ using namespace std::string_literals; using namespace TestCommon; using namespace AppInstaller::Portable; using namespace AppInstaller::Repository::Microsoft; -using namespace AppInstaller::Repository::SQLite; +using namespace AppInstaller::SQLite; using namespace AppInstaller::Repository::Microsoft::Schema; void CreateFakePortableFile(PortableFileEntry& file) @@ -29,11 +29,11 @@ TEST_CASE("PortableIndexCreateLatestAndReopen", "[portableIndex]") TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); - Schema::Version versionCreated; + Version versionCreated; // Create the index { - PortableIndex index = PortableIndex::CreateNew(tempFile, Schema::Version::Latest()); + PortableIndex index = PortableIndex::CreateNew(tempFile, Version::Latest()); versionCreated = index.GetVersion(); } @@ -41,7 +41,7 @@ TEST_CASE("PortableIndexCreateLatestAndReopen", "[portableIndex]") { INFO("Trying with Read"); PortableIndex index = PortableIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::Read); - Schema::Version versionRead = index.GetVersion(); + Version versionRead = index.GetVersion(); REQUIRE(versionRead == versionCreated); } @@ -49,7 +49,7 @@ TEST_CASE("PortableIndexCreateLatestAndReopen", "[portableIndex]") { INFO("Trying with ReadWrite"); PortableIndex index = PortableIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); - Schema::Version versionRead = index.GetVersion(); + Version versionRead = index.GetVersion(); REQUIRE(versionRead == versionCreated); } @@ -57,7 +57,7 @@ TEST_CASE("PortableIndexCreateLatestAndReopen", "[portableIndex]") { INFO("Trying with Immutable"); PortableIndex index = PortableIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::Immutable); - Schema::Version versionRead = index.GetVersion(); + Version versionRead = index.GetVersion(); REQUIRE(versionRead == versionCreated); } } diff --git a/src/AppInstallerCLITests/PortableInstaller.cpp b/src/AppInstallerCLITests/PortableInstaller.cpp @@ -8,7 +8,7 @@ #include <winget/Manifest.h> #include <winget/PathVariable.h> #include <winget/PortableARPEntry.h> -#include <Microsoft/SQLiteStorageBase.h> +#include <winget/SQLiteStorageBase.h> #include <Microsoft/Schema/IPortableIndex.h> #include <Microsoft/PortableIndex.h> @@ -18,7 +18,7 @@ using namespace AppInstaller::Filesystem; using namespace AppInstaller::Manifest; using namespace AppInstaller::Registry::Environment; using namespace AppInstaller::Repository::Microsoft; -using namespace AppInstaller::Repository::SQLite; +using namespace AppInstaller::SQLite; using namespace AppInstaller::Repository::Microsoft::Schema; using namespace AppInstaller::Utility; using namespace TestCommon; diff --git a/src/AppInstallerCLITests/SQLiteIndex.cpp b/src/AppInstallerCLITests/SQLiteIndex.cpp @@ -2,7 +2,7 @@ // Licensed under the MIT License. #include "pch.h" #include "TestCommon.h" -#include <SQLiteWrapper.h> +#include <winget/SQLiteWrapper.h> #include <PackageDependenciesValidation.h> #include <ArpVersionValidation.h> #include <Microsoft/SQLiteIndex.h> @@ -27,41 +27,44 @@ using namespace TestCommon; using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; using namespace AppInstaller::Repository::Microsoft; -using namespace AppInstaller::Repository::SQLite; +using namespace AppInstaller::SQLite; using namespace AppInstaller::Utility; -SQLiteIndex CreateTestIndex(const std::string& filePath, std::optional<Schema::Version> version = {}) +using UtilityVersion = AppInstaller::Utility::Version; +using SQLiteVersion = AppInstaller::SQLite::Version; + +SQLiteIndex CreateTestIndex(const std::string& filePath, std::optional<SQLiteVersion> version = {}) { // If no specific version requested, then use generator to run against the last 3 versions. if (!version) { - Schema::Version latestVersion = SQLiteIndex::GetLatestVersion(); + SQLiteVersion latestVersion = SQLiteIndex::GetLatestVersion(); if (latestVersion.MajorVersion != 1) { throw std::exception("You added major version 2, figure out how to deal with these tests that do back compat coverage!"); } // Relies on the fact that min version is already >= 2 - Schema::Version versionMinus1 = Schema::Version{ 1, latestVersion.MinorVersion - 1 }; - Schema::Version versionMinus2 = Schema::Version{ 1, latestVersion.MinorVersion - 2 }; + SQLiteVersion versionMinus1 = SQLiteVersion{ 1, latestVersion.MinorVersion - 1 }; + SQLiteVersion versionMinus2 = SQLiteVersion{ 1, latestVersion.MinorVersion - 2 }; - version = GENERATE_COPY(Schema::Version{ versionMinus2 }, Schema::Version{ versionMinus1 }, Schema::Version{ latestVersion }); + version = GENERATE_COPY(SQLiteVersion{ versionMinus2 }, SQLiteVersion{ versionMinus1 }, SQLiteVersion{ latestVersion }); } return SQLiteIndex::CreateNew(filePath, version.value()); } -Schema::Version TestPrepareForRead(SQLiteIndex& index) +SQLiteVersion TestPrepareForRead(SQLiteIndex& index) { - Schema::Version latestVersion = SQLiteIndex::GetLatestVersion(); + SQLiteVersion latestVersion = SQLiteIndex::GetLatestVersion(); if (latestVersion.MajorVersion != 1) { throw std::exception("You added major version 2, figure out how to deal with these tests that do back compat coverage!"); } // Relies on the fact that min version is already >= 2 - Schema::Version versionMinus1 = Schema::Version{ 1, latestVersion.MinorVersion - 1 }; - Schema::Version versionMinus2 = Schema::Version{ 1, latestVersion.MinorVersion - 2 }; + SQLiteVersion versionMinus1 = SQLiteVersion{ 1, latestVersion.MinorVersion - 1 }; + SQLiteVersion versionMinus2 = SQLiteVersion{ 1, latestVersion.MinorVersion - 2 }; if (index.GetVersion() == versionMinus2) { @@ -69,7 +72,7 @@ Schema::Version TestPrepareForRead(SQLiteIndex& index) } else if (index.GetVersion() == versionMinus1) { - Schema::Version version = GENERATE_COPY(Schema::Version{ versionMinus2 }, Schema::Version{ versionMinus1 }); + SQLiteVersion version = GENERATE_COPY(SQLiteVersion{ versionMinus2 }, SQLiteVersion{ versionMinus1 }); if (version != versionMinus1) { @@ -79,7 +82,7 @@ Schema::Version TestPrepareForRead(SQLiteIndex& index) } else if (index.GetVersion() == latestVersion) { - Schema::Version version = GENERATE_COPY(Schema::Version{ versionMinus2 }, Schema::Version{ versionMinus1 }, Schema::Version{ latestVersion }); + SQLiteVersion version = GENERATE_COPY(SQLiteVersion{ versionMinus2 }, SQLiteVersion{ versionMinus1 }, SQLiteVersion{ latestVersion }); if (version != latestVersion) { @@ -111,7 +114,7 @@ void CreateFakeManifest(Manifest& manifest, string_t publisher, string_t version manifest.Installers[0].Commands = { "test1", "test2" }; } -SQLiteIndex SimpleTestSetup(const std::string& filePath, Manifest& manifest, std::optional<Schema::Version> version = {}) +SQLiteIndex SimpleTestSetup(const std::string& filePath, Manifest& manifest, std::optional<SQLiteVersion> version = {}) { SQLiteIndex index = CreateTestIndex(filePath, version); @@ -242,7 +245,7 @@ struct IndexFields std::string ArpPublisher; }; -SQLiteIndex SearchTestSetup(const std::string& filePath, std::initializer_list<IndexFields> data = {}, std::optional<Schema::Version> version = {}) +SQLiteIndex SearchTestSetup(const std::string& filePath, std::initializer_list<IndexFields> data = {}, std::optional<SQLiteVersion> version = {}) { SQLiteIndex index = CreateTestIndex(filePath, version); @@ -295,48 +298,48 @@ SQLiteIndex SearchTestSetup(const std::string& filePath, std::initializer_list<I return index; } -bool ArePackageFamilyNameAndProductCodeSupported(const SQLiteIndex& index, const Schema::Version& testVersion) +bool ArePackageFamilyNameAndProductCodeSupported(const SQLiteIndex& index, const SQLiteVersion& testVersion) { UNSCOPED_INFO("Index " << index.GetVersion() << " | Test " << testVersion); - return (index.GetVersion() >= Schema::Version{ 1, 1 } && testVersion >= Schema::Version{ 1, 1 }); + return (index.GetVersion() >= SQLiteVersion{ 1, 1 } && testVersion >= SQLiteVersion{ 1, 1 }); } -bool AreNormalizedNameAndPublisherSupported(const SQLiteIndex& index, const Schema::Version& testVersion) +bool AreNormalizedNameAndPublisherSupported(const SQLiteIndex& index, const SQLiteVersion& testVersion) { UNSCOPED_INFO("Index " << index.GetVersion() << " | Test " << testVersion); - return (index.GetVersion() >= Schema::Version{ 1, 2 } && testVersion >= Schema::Version{ 1, 2 }); + return (index.GetVersion() >= SQLiteVersion{ 1, 2 } && testVersion >= SQLiteVersion{ 1, 2 }); } -bool IsManifestMetadataSupported(const SQLiteIndex& index, const Schema::Version& testVersion) +bool IsManifestMetadataSupported(const SQLiteIndex& index, const SQLiteVersion& testVersion) { UNSCOPED_INFO("Index " << index.GetVersion() << " | Test " << testVersion); - return (index.GetVersion() >= Schema::Version{ 1, 1 } && testVersion >= Schema::Version{ 1, 1 }); + return (index.GetVersion() >= SQLiteVersion{ 1, 1 } && testVersion >= SQLiteVersion{ 1, 1 }); } -bool AreManifestHashesSupported(const SQLiteIndex& index, const Schema::Version& testVersion) +bool AreManifestHashesSupported(const SQLiteIndex& index, const SQLiteVersion& testVersion) { UNSCOPED_INFO("Index " << index.GetVersion() << " | Test " << testVersion); - return (index.GetVersion() >= Schema::Version{ 1, 3 } && testVersion >= Schema::Version{ 1, 3 }); + return (index.GetVersion() >= SQLiteVersion{ 1, 3 } && testVersion >= SQLiteVersion{ 1, 3 }); } -bool AreArpVersionsSupported(const SQLiteIndex& index, const Schema::Version& testVersion) +bool AreArpVersionsSupported(const SQLiteIndex& index, const SQLiteVersion& testVersion) { UNSCOPED_INFO("Index " << index.GetVersion() << " | Test " << testVersion); - return (index.GetVersion() >= Schema::Version{ 1, 5 } && testVersion >= Schema::Version{ 1, 5 }); + return (index.GetVersion() >= SQLiteVersion{ 1, 5 } && testVersion >= SQLiteVersion{ 1, 5 }); } -bool IsMapDataFoldingSupported(const SQLiteIndex& index, const Schema::Version& testVersion) +bool IsMapDataFoldingSupported(const SQLiteIndex& index, const SQLiteVersion& testVersion) { UNSCOPED_INFO("Index " << index.GetVersion() << " | Test " << testVersion); - return (index.GetVersion() >= Schema::Version{ 1, 7 } && testVersion >= Schema::Version{ 1, 7 }); + return (index.GetVersion() >= SQLiteVersion{ 1, 7 } && testVersion >= SQLiteVersion{ 1, 7 }); } bool IsMapDataFolded(const SQLiteIndex& index) { - return (index.GetVersion() >= Schema::Version{ 1, 7 }); + return (index.GetVersion() >= SQLiteVersion{ 1, 7 }); } -std::string GetPropertyStringByKey(const SQLiteIndex& index, SQLite::rowid_t id, PackageVersionProperty property, std::string_view version, std::string_view channel) +std::string GetPropertyStringByKey(const SQLiteIndex& index, rowid_t id, PackageVersionProperty property, std::string_view version, std::string_view channel) { auto manifestId = index.GetManifestIdByKey(id, version, channel); REQUIRE(manifestId); @@ -345,24 +348,24 @@ std::string GetPropertyStringByKey(const SQLiteIndex& index, SQLite::rowid_t id, return result.value(); } -std::string GetPropertyStringById(const SQLiteIndex& index, SQLite::rowid_t id, PackageVersionProperty property) +std::string GetPropertyStringById(const SQLiteIndex& index, rowid_t id, PackageVersionProperty property) { auto versions = index.GetVersionKeysById(id); REQUIRE(!versions.empty()); return GetPropertyStringByKey(index, id, property, versions[0].GetVersion().ToString(), versions[0].GetChannel().ToString()); } -std::string GetIdStringById(const SQLiteIndex& index, SQLite::rowid_t id) +std::string GetIdStringById(const SQLiteIndex& index, rowid_t id) { return GetPropertyStringById(index, id, PackageVersionProperty::Id); } -std::string GetNameStringById(const SQLiteIndex& index, SQLite::rowid_t id) +std::string GetNameStringById(const SQLiteIndex& index, rowid_t id) { return GetPropertyStringById(index, id, PackageVersionProperty::Name); } -std::string GetPathStringByKey(const SQLiteIndex& index, SQLite::rowid_t id, std::string_view version, std::string_view channel) +std::string GetPathStringByKey(const SQLiteIndex& index, rowid_t id, std::string_view version, std::string_view channel) { return GetPropertyStringByKey(index, id, PackageVersionProperty::RelativePath, version, channel); } @@ -372,11 +375,11 @@ TEST_CASE("SQLiteIndexCreateLatestAndReopen", "[sqliteindex]") TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); - Schema::Version versionCreated; + SQLiteVersion versionCreated; // Create the index { - SQLiteIndex index = SQLiteIndex::CreateNew(tempFile, Schema::Version::Latest()); + SQLiteIndex index = SQLiteIndex::CreateNew(tempFile, SQLiteVersion::Latest()); versionCreated = index.GetVersion(); } @@ -384,7 +387,7 @@ TEST_CASE("SQLiteIndexCreateLatestAndReopen", "[sqliteindex]") { INFO("Trying with Read"); SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::Read); - Schema::Version versionRead = index.GetVersion(); + SQLiteVersion versionRead = index.GetVersion(); REQUIRE(versionRead == versionCreated); } @@ -392,7 +395,7 @@ TEST_CASE("SQLiteIndexCreateLatestAndReopen", "[sqliteindex]") { INFO("Trying with ReadWrite"); SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); - Schema::Version versionRead = index.GetVersion(); + SQLiteVersion versionRead = index.GetVersion(); REQUIRE(versionRead == versionCreated); } @@ -400,7 +403,7 @@ TEST_CASE("SQLiteIndexCreateLatestAndReopen", "[sqliteindex]") { INFO("Trying with Immutable"); SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::Immutable); - Schema::Version versionRead = index.GetVersion(); + SQLiteVersion versionRead = index.GetVersion(); REQUIRE(versionRead == versionCreated); } } @@ -413,7 +416,7 @@ TEST_CASE("SQLiteIndexCreateAndAddManifest", "[sqliteindex]") Manifest manifest; std::string relativePath = "test/id/1.0.0.yaml"; - SQLiteIndex index = SimpleTestSetup(tempFile, manifest, Schema::Version::Latest()); + SQLiteIndex index = SimpleTestSetup(tempFile, manifest, SQLiteVersion::Latest()); } TEST_CASE("SQLiteIndexCreateAndAddManifestFile", "[sqliteindex]") @@ -457,7 +460,7 @@ TEST_CASE("SQLiteIndex_VersionReferencedByDependenciesClearsUnusedVersionAndKeep INFO("Using temporary file named: " << tempFile.GetPath()); Manifest dependencyManifest1, dependencyManifest2, manifest, isolatedManifest; - SQLiteIndex index = SimpleTestSetup(tempFile, dependencyManifest1, Schema::Version::Latest()); + SQLiteIndex index = SimpleTestSetup(tempFile, dependencyManifest1, SQLiteVersion::Latest()); auto& publisher2 = "Test2"; CreateFakeManifest(dependencyManifest2, publisher2); @@ -506,7 +509,7 @@ TEST_CASE("SQLiteIndex_AddUpdateRemoveManifestWithDependencies", "[sqliteindex][ INFO("Using temporary file named: " << tempFile.GetPath()); Manifest dependencyManifest1, dependencyManifest2, manifest; - SQLiteIndex index = SimpleTestSetup(tempFile, dependencyManifest1, Schema::Version::Latest()); + SQLiteIndex index = SimpleTestSetup(tempFile, dependencyManifest1, SQLiteVersion::Latest()); auto& publisher2 = "Test2"; CreateFakeManifest(dependencyManifest2, publisher2); @@ -528,7 +531,7 @@ TEST_CASE("SQLiteIndex_AddManifestWithDependencies_MissingPackage", "[sqliteinde INFO("Using temporary file named: " << tempFile.GetPath()); Manifest dependencyManifest1, dependencyManifest2, manifest; - SQLiteIndex index = SimpleTestSetup(tempFile, dependencyManifest1, Schema::Version::Latest()); + SQLiteIndex index = SimpleTestSetup(tempFile, dependencyManifest1, SQLiteVersion::Latest()); // Publisher2 is not present auto& publisher2 = "Test2"; @@ -548,7 +551,7 @@ TEST_CASE("SQLiteIndex_AddUpdateRemoveManifestWithDependencies_MissingVersion", INFO("Using temporary file named: " << tempFile.GetPath()); Manifest dependencyManifest1, dependencyManifest2, manifest; - SQLiteIndex index = SimpleTestSetup(tempFile, dependencyManifest1, Schema::Version::Latest()); + SQLiteIndex index = SimpleTestSetup(tempFile, dependencyManifest1, SQLiteVersion::Latest()); auto& publisher2 = "Test2"; CreateFakeManifest(dependencyManifest2, publisher2); @@ -570,7 +573,7 @@ TEST_CASE("SQLiteIndex_AddUpdateRemoveManifestWithDependencies_EmptyManifestVers INFO("Using temporary file named: " << tempFile.GetPath()); Manifest dependencyManifest1, dependencyManifest2, manifest; - SQLiteIndex index = SimpleTestSetup(tempFile, dependencyManifest1, Schema::Version::Latest()); + SQLiteIndex index = SimpleTestSetup(tempFile, dependencyManifest1, SQLiteVersion::Latest()); auto& publisher2 = "Test2"; CreateFakeManifest(dependencyManifest2, publisher2); @@ -593,7 +596,7 @@ TEST_CASE("SQLiteIndex_DependenciesTable_CheckConsistency", "[sqliteindex][V1_4] { Manifest levelOneManifest, levelTwoManifest, levelThreeManifest, topLevelManifest; - SQLiteIndex index = SimpleTestSetup(tempFile, levelThreeManifest, Schema::Version::Latest()); + SQLiteIndex index = SimpleTestSetup(tempFile, levelThreeManifest, SQLiteVersion::Latest()); constexpr std::string_view levelTwoManifestPublisher = "LevelTwoManifest"; CreateFakeManifest(levelTwoManifest, levelTwoManifestPublisher); @@ -614,12 +617,12 @@ TEST_CASE("SQLiteIndex_DependenciesTable_CheckConsistency", "[sqliteindex][V1_4] { // Open it directly to modify the table Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); - SQLite::rowid_t nonExistentRowId = 40; - SQLite::rowid_t nonExistentManifest = 41; - SQLite::rowid_t nonExistentVersion = 42; - SQLite::rowid_t nonExistentPackageId = 43; + rowid_t nonExistentRowId = 40; + rowid_t nonExistentManifest = 41; + rowid_t nonExistentVersion = 42; + rowid_t nonExistentPackageId = 43; - SQLite::Builder::StatementBuilder builder; + Builder::StatementBuilder builder; builder.InsertInto(Schema::V1_4::DependenciesTable::TableName()) .Values(nonExistentRowId, nonExistentManifest, nonExistentVersion, nonExistentPackageId); builder.Execute(connection); @@ -635,7 +638,7 @@ TEST_CASE("SQLiteIndex_DependenciesTable_CheckConsistency", "[sqliteindex][V1_4] INFO("Using temporary file named: " << tempFile2.GetPath()); { - SQLiteIndex index = CreateTestIndex(tempFile2, Schema::Version::Latest()); + SQLiteIndex index = CreateTestIndex(tempFile2, SQLiteVersion::Latest()); Manifest manifest; manifest.Id = "Foo"; @@ -770,7 +773,7 @@ TEST_CASE("SQLiteIndex_RemoveManifestWithDependencies", "[sqliteindex][V1_4]") INFO("Using temporary file named: " << tempFile.GetPath()); Manifest dependencyManifest1, dependencyManifest2, manifest; - SQLiteIndex index = SimpleTestSetup(tempFile, dependencyManifest1, Schema::Version::Latest()); + SQLiteIndex index = SimpleTestSetup(tempFile, dependencyManifest1, SQLiteVersion::Latest()); auto& publisher2 = "Test2"; CreateFakeManifest(dependencyManifest2, publisher2); @@ -792,7 +795,7 @@ TEST_CASE("SQLiteIndex_ValidateManifestWithDependencies", "[sqliteindex][V1_4]") INFO("Using temporary file named: " << tempFile.GetPath()); Manifest levelOneManifest, levelTwoManifest, levelThreeManifest, topLevelManifest; - SQLiteIndex index = SimpleTestSetup(tempFile, levelThreeManifest, Schema::Version::Latest()); + SQLiteIndex index = SimpleTestSetup(tempFile, levelThreeManifest, SQLiteVersion::Latest()); constexpr std::string_view levelTwoManifestPublisher = "LevelTwoManifest"; CreateFakeManifest(levelTwoManifest, levelTwoManifestPublisher); @@ -817,7 +820,7 @@ TEST_CASE("SQLiteIndex_ValidateManifestWithDependenciesHasLoops", "[sqliteindex] INFO("Using temporary file named: " << tempFile.GetPath()); Manifest levelOneManifest, levelTwoManifest, levelThreeManifest, topLevelManifest; - SQLiteIndex index = SimpleTestSetup(tempFile, levelThreeManifest, Schema::Version::Latest()); + SQLiteIndex index = SimpleTestSetup(tempFile, levelThreeManifest, SQLiteVersion::Latest()); constexpr std::string_view levelTwoManifestPublisher = "LevelTwoManifest"; CreateFakeManifest(levelTwoManifest, levelTwoManifestPublisher); @@ -848,7 +851,7 @@ TEST_CASE("SQLiteIndex_ValidateManifestWithDependenciesMissingNode", "[sqliteind INFO("Using temporary file named: " << tempFile.GetPath()); Manifest levelOneManifest, levelTwoManifest, levelThreeManifest, topLevelManifest; - SQLiteIndex index = SimpleTestSetup(tempFile, levelThreeManifest, Schema::Version::Latest()); + SQLiteIndex index = SimpleTestSetup(tempFile, levelThreeManifest, SQLiteVersion::Latest()); constexpr std::string_view levelTwoManifestPublisher = "LevelTwoManifest"; CreateFakeManifest(levelTwoManifest, levelTwoManifestPublisher); @@ -875,7 +878,7 @@ TEST_CASE("SQLiteIndex_ValidateManifestWithDependenciesNoSuitableMinVersion", "[ INFO("Using temporary file named: " << tempFile.GetPath()); Manifest levelOneManifest, levelTwoManifest, levelThreeManifest, topLevelManifest; - SQLiteIndex index = SimpleTestSetup(tempFile, levelThreeManifest, Schema::Version::Latest()); + SQLiteIndex index = SimpleTestSetup(tempFile, levelThreeManifest, SQLiteVersion::Latest()); constexpr std::string_view levelTwoManifestPublisher = "LevelTwoManifest"; CreateFakeManifest(levelTwoManifest, levelTwoManifestPublisher); @@ -903,7 +906,7 @@ TEST_CASE("SQLiteIndex_ValidateManifestWhenManifestIsDependency_StructureBroken" INFO("Using temporary file named: " << tempFile.GetPath()); Manifest levelOneManifest, levelTwoManifest, levelThreeManifest, topLevelManifest; - SQLiteIndex index = SimpleTestSetup(tempFile, levelThreeManifest, Schema::Version::Latest()); + SQLiteIndex index = SimpleTestSetup(tempFile, levelThreeManifest, SQLiteVersion::Latest()); constexpr std::string_view levelTwoManifestPublisher = "LevelTwoManifest"; CreateFakeManifest(levelTwoManifest, levelTwoManifestPublisher); @@ -932,7 +935,7 @@ TEST_CASE("SQLiteIndex_ValidateManifestWhenManifestIsDependency_StructureNotBrok INFO("Using temporary file named: " << tempFile.GetPath()); Manifest levelOneManifest, levelTwoManifest, levelThreeManifest, topLevelManifest, levelThreeManifestV2; - SQLiteIndex index = SimpleTestSetup(tempFile, levelThreeManifest, Schema::Version::Latest()); + SQLiteIndex index = SimpleTestSetup(tempFile, levelThreeManifest, SQLiteVersion::Latest()); constexpr std::string_view levelTwoManifestPublisher = "LevelTwoManifest"; CreateFakeManifest(levelTwoManifest, levelTwoManifestPublisher); @@ -963,7 +966,7 @@ TEST_CASE("SQLiteIndex_ValidateManifestWhenManifestIsDependency_StructureBroken_ INFO("Using temporary file named: " << tempFile.GetPath()); Manifest levelOneManifest, levelTwoManifest, levelThreeManifest, topLevelManifest, levelThreeManifestV2; - SQLiteIndex index = SimpleTestSetup(tempFile, levelThreeManifest, Schema::Version::Latest()); + SQLiteIndex index = SimpleTestSetup(tempFile, levelThreeManifest, SQLiteVersion::Latest()); constexpr std::string_view levelThreeManifestV2Publisher = "Test"; CreateFakeManifest(levelThreeManifestV2, levelThreeManifestV2Publisher, "2.0.0"); @@ -1106,7 +1109,7 @@ TEST_CASE("SQLiteIndex_UpdateManifest", "[sqliteindex][V1_4]") { - auto version = GENERATE(Schema::Version{ 1, 0 }, Schema::Version::Latest()); + auto version = GENERATE(SQLiteVersion{ 1, 0 }, SQLiteVersion::Latest()); SQLiteIndex index = SQLiteIndex::CreateNew(tempFile, version); index.AddManifest(manifest, manifestPath); @@ -1190,7 +1193,7 @@ TEST_CASE("SQLiteIndex_UpdateManifestWithDependencies", "[sqliteindex][V1_4]") INFO("Using temporary file named: " << tempFile.GetPath()); Manifest dependencyManifest1, dependencyManifest2, manifest, updateManifest; - SQLiteIndex index = SimpleTestSetup(tempFile, dependencyManifest1, Schema::Version::Latest()); + SQLiteIndex index = SimpleTestSetup(tempFile, dependencyManifest1, SQLiteVersion::Latest()); auto& publisher2 = "Test2"; CreateFakeManifest(dependencyManifest2, publisher2); @@ -1219,7 +1222,7 @@ TEST_CASE("SQLiteIndex_UpdateManifestWithDependenciesDeleteAndAdd", "[sqliteinde INFO("Using temporary file named: " << tempFile.GetPath()); Manifest dependencyManifest1, dependencyManifest2, manifest, updateManifest; - SQLiteIndex index = SimpleTestSetup(tempFile, dependencyManifest1, Schema::Version::Latest()); + SQLiteIndex index = SimpleTestSetup(tempFile, dependencyManifest1, SQLiteVersion::Latest()); auto& publisher2 = "Test2"; CreateFakeManifest(dependencyManifest2, publisher2); @@ -1604,8 +1607,8 @@ TEST_CASE("PathPartTable_EnsurePathExists", "[sqliteindex][V1_0]") // Create the index { SQLiteIndex index = SQLiteIndex::CreateNew(tempFile, { 1, 0 }); - Schema::Version versionCreated = index.GetVersion(); - REQUIRE(versionCreated == Schema::Version{ 1, 0 }); + SQLiteVersion versionCreated = index.GetVersion(); + REQUIRE(versionCreated == SQLiteVersion{ 1, 0 }); } // Open it directly to directly test pathpart table @@ -1847,14 +1850,14 @@ TEST_CASE("SQLiteIndex_Search_VersionSorting", "[sqliteindex]") std::vector<VersionAndChannel> sortedList = { - { Version("15.0.0"), Channel("") }, - { Version("14.0.0"), Channel("") }, - { Version("13.2.0-bugfix"), Channel("") }, - { Version("13.2.0"), Channel("") }, - { Version("13.0.0"), Channel("") }, - { Version("16.0.0"), Channel("alpha") }, - { Version("15.8.0"), Channel("alpha") }, - { Version("15.1.0"), Channel("beta") }, + { UtilityVersion("15.0.0"), Channel("") }, + { UtilityVersion("14.0.0"), Channel("") }, + { UtilityVersion("13.2.0-bugfix"), Channel("") }, + { UtilityVersion("13.2.0"), Channel("") }, + { UtilityVersion("13.0.0"), Channel("") }, + { UtilityVersion("16.0.0"), Channel("alpha") }, + { UtilityVersion("15.8.0"), Channel("alpha") }, + { UtilityVersion("15.1.0"), Channel("beta") }, }; SQLiteIndex index = SearchTestSetup(tempFile, { @@ -1897,14 +1900,14 @@ TEST_CASE("SQLiteIndex_PathString_VersionSorting", "[sqliteindex]") std::vector<VersionAndChannel> sortedList = { - { Version("15.0.0"), Channel("") }, - { Version("14.0.0"), Channel("") }, - { Version("13.2.0-bugfix"), Channel("") }, - { Version("13.2.0"), Channel("") }, - { Version("13.0.0"), Channel("") }, - { Version("16.0.0"), Channel("alpha") }, - { Version("15.8.0"), Channel("alpha") }, - { Version("15.1.0"), Channel("beta") }, + { UtilityVersion("15.0.0"), Channel("") }, + { UtilityVersion("14.0.0"), Channel("") }, + { UtilityVersion("13.2.0-bugfix"), Channel("") }, + { UtilityVersion("13.2.0"), Channel("") }, + { UtilityVersion("13.0.0"), Channel("") }, + { UtilityVersion("16.0.0"), Channel("alpha") }, + { UtilityVersion("15.8.0"), Channel("alpha") }, + { UtilityVersion("15.1.0"), Channel("beta") }, }; SQLiteIndex index = SearchTestSetup(tempFile, { @@ -1980,7 +1983,7 @@ TEST_CASE("SQLiteIndex_SearchResultsTableSearches", "[sqliteindex][V1_0]") Manifest manifest; { - (void)SimpleTestSetup(tempFile, manifest, Schema::Version{ 1, 0 }); + (void)SimpleTestSetup(tempFile, manifest, SQLiteVersion{ 1, 0 }); } Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadOnly); @@ -2412,7 +2415,7 @@ TEST_CASE("SQLiteIndex_Search_Query_PackageFamilyNameSubstring", "[sqliteindex]" { "Id3", "Name3", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3", { "PFN3" }, { "PC3" } }, }); - Schema::Version testVersion = TestPrepareForRead(index); + SQLiteVersion testVersion = TestPrepareForRead(index); SearchRequest request; request.Query = RequestMatch(MatchType::Substring, "PFN"); @@ -2432,7 +2435,7 @@ TEST_CASE("SQLiteIndex_Search_Query_ProductCodeSubstring", "[sqliteindex]") { "Id3", "Name3", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3", { "PFN3" }, { "PC3" } }, }); - Schema::Version testVersion = TestPrepareForRead(index); + SQLiteVersion testVersion = TestPrepareForRead(index); SearchRequest request; request.Query = RequestMatch(MatchType::Substring, "PC"); @@ -2452,7 +2455,7 @@ TEST_CASE("SQLiteIndex_Search_Query_PackageFamilyNameMatch", "[sqliteindex]") { "Id3", "Name3", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3", { "PFN3" }, { "PC3" } }, }); - Schema::Version testVersion = TestPrepareForRead(index); + SQLiteVersion testVersion = TestPrepareForRead(index); SearchRequest request; request.Query = RequestMatch(MatchType::Substring, "pfn1"); @@ -2480,7 +2483,7 @@ TEST_CASE("SQLiteIndex_Search_Query_ProductCodeMatch", "[sqliteindex]") { "Id3", "Name3", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3", { "PFN3" }, { "PC3" } }, }); - Schema::Version testVersion = TestPrepareForRead(index); + SQLiteVersion testVersion = TestPrepareForRead(index); SearchRequest request; request.Query = RequestMatch(MatchType::Substring, "pc2"); @@ -2508,7 +2511,7 @@ TEST_CASE("SQLiteIndex_Search_PackageFamilyNameSubstring", "[sqliteindex]") { "Id3", "Name3", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3", { "PFN3" }, { "PC3" } }, }); - Schema::Version testVersion = TestPrepareForRead(index); + SQLiteVersion testVersion = TestPrepareForRead(index); SearchRequest request; request.Inclusions.emplace_back(PackageMatchField::PackageFamilyName, MatchType::Substring, "PFN"); @@ -2536,7 +2539,7 @@ TEST_CASE("SQLiteIndex_Search_ProductCodeSubstring", "[sqliteindex]") { "Id3", "Name3", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3", { "PFN3" }, { "PC3" } }, }); - Schema::Version testVersion = TestPrepareForRead(index); + SQLiteVersion testVersion = TestPrepareForRead(index); SearchRequest request; request.Inclusions.emplace_back(PackageMatchField::ProductCode, MatchType::Substring, "PC"); @@ -2564,7 +2567,7 @@ TEST_CASE("SQLiteIndex_Search_PackageFamilyNameMatch", "[sqliteindex]") { "Id3", "Name3", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3", { "PFN3" }, { "PC3" } }, }); - Schema::Version testVersion = TestPrepareForRead(index); + SQLiteVersion testVersion = TestPrepareForRead(index); SearchRequest request; request.Inclusions.emplace_back(PackageMatchField::PackageFamilyName, MatchType::Exact, "pfn1"); @@ -2592,7 +2595,7 @@ TEST_CASE("SQLiteIndex_Search_ProductCodeMatch", "[sqliteindex]") { "Id3", "Name3", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3", { "PFN3" }, { "PC3" } }, }); - Schema::Version testVersion = TestPrepareForRead(index); + SQLiteVersion testVersion = TestPrepareForRead(index); SearchRequest request; request.Inclusions.emplace_back(PackageMatchField::ProductCode, MatchType::Exact, "pc2"); @@ -2636,7 +2639,7 @@ TEST_CASE("SQLiteIndex_CheckConsistency_Failure", "[sqliteindex][V1_1]") manifest2.DefaultLocalization.Add<Localization::Tags>({}); manifest2.Installers[0].Commands = { "test1", "test2", "test3" }; - SQLite::rowid_t manifestRowId = 0; + rowid_t manifestRowId = 0; { SQLiteIndex index = SQLiteIndex::CreateNew(tempFile, { 1, 1 }); @@ -2657,8 +2660,8 @@ TEST_CASE("SQLiteIndex_CheckConsistency_Failure", "[sqliteindex][V1_1]") // Open it directly to modify the table Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); - SQLite::Builder::StatementBuilder builder; - builder.DeleteFrom(Schema::V1_0::IdTable::TableName()).Where(SQLite::RowIDName).Equals(manifestRowId); + Builder::StatementBuilder builder; + builder.DeleteFrom(Schema::V1_0::IdTable::TableName()).Where(RowIDName).Equals(manifestRowId); builder.Execute(connection); } @@ -2678,7 +2681,7 @@ TEST_CASE("SQLiteIndex_GetMultiProperty_PackageFamilyName", "[sqliteindex]") { "Id1", "Name1", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1", { "PFN1", "PFN2" }, {} }, }); - Schema::Version testVersion = TestPrepareForRead(index); + SQLiteVersion testVersion = TestPrepareForRead(index); SearchRequest request; @@ -2708,7 +2711,7 @@ TEST_CASE("SQLiteIndex_GetMultiProperty_ProductCode", "[sqliteindex]") { "Id1", "Name1", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1", {}, { "PC1", "PC2" } }, }); - Schema::Version testVersion = TestPrepareForRead(index); + SQLiteVersion testVersion = TestPrepareForRead(index); SearchRequest request; @@ -2739,7 +2742,7 @@ TEST_CASE("SQLiteIndex_ManifestMetadata", "[sqliteindex]") { "Id2", "Name2", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path2", { "PFN1", "PFN2" }, {} }, }); - Schema::Version testVersion = TestPrepareForRead(index); + SQLiteVersion testVersion = TestPrepareForRead(index); SearchRequest request; @@ -2785,7 +2788,7 @@ TEST_CASE("SQLiteIndex_NormNameAndPublisher_Exact", "[sqliteindex]") { "Id1", testName, testPublisher, "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1", {}, { "PC1", "PC2" } }, }); - Schema::Version testVersion = TestPrepareForRead(index); + SQLiteVersion testVersion = TestPrepareForRead(index); SearchRequest request; request.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::NormalizedNameAndPublisher, MatchType::Exact, testName, testPublisher)); @@ -2814,7 +2817,7 @@ TEST_CASE("SQLiteIndex_NormNameAndPublisher_Simple", "[sqliteindex]") { "Id1", testName, testPublisher, "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1", {}, { "PC1", "PC2" } }, }); - Schema::Version testVersion = TestPrepareForRead(index); + SQLiteVersion testVersion = TestPrepareForRead(index); SearchRequest request; request.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::NormalizedNameAndPublisher, MatchType::Exact, testName + " 1.0", testPublisher + " Corporation")); @@ -2844,7 +2847,7 @@ TEST_CASE("SQLiteIndex_NormNameAndPublisher_Complex", "[sqliteindex]") { "Id2", testName, "Different Publisher", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path2", {}, { "PC1", "PC2" } }, }); - Schema::Version testVersion = TestPrepareForRead(index); + SQLiteVersion testVersion = TestPrepareForRead(index); SearchRequest request; request.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::NormalizedNameAndPublisher, MatchType::Exact, testName + " 1.0", testPublisher)); @@ -2875,7 +2878,7 @@ TEST_CASE("SQLiteIndex_NormNameAndPublisher_AppsAndFeatures", "[sqliteindex]") { "Id1", testName, testPublisher, "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1", {}, { "PC1", "PC2" }, arpTestName, arpTestPublisher }, }); - Schema::Version testVersion = TestPrepareForRead(index); + SQLiteVersion testVersion = TestPrepareForRead(index); SearchRequest request; request.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::NormalizedNameAndPublisher, MatchType::Exact, arpTestName, arpTestPublisher)); @@ -2908,7 +2911,7 @@ TEST_CASE("SQLiteIndex_ManifestHash_Present", "[sqliteindex]") manifest.StreamSha256 = hash; index.AddManifest(manifest, "path"); - Schema::Version testVersion = TestPrepareForRead(index); + SQLiteVersion testVersion = TestPrepareForRead(index); auto results = index.Search({}); REQUIRE(results.Matches.size() == 1); @@ -2940,7 +2943,7 @@ TEST_CASE("SQLiteIndex_ManifestHash_Missing", "[sqliteindex]") manifest.Version = "Bar"; index.AddManifest(manifest, "path"); - Schema::Version testVersion = TestPrepareForRead(index); + SQLiteVersion testVersion = TestPrepareForRead(index); auto results = index.Search({}); REQUIRE(results.Matches.size() == 1); @@ -2969,7 +2972,7 @@ TEST_CASE("SQLiteIndex_ManifestArpVersion_Present_Add", "[sqliteindex]") index.AddManifest(manifest, "path"); - Schema::Version testVersion = TestPrepareForRead(index); + SQLiteVersion testVersion = TestPrepareForRead(index); auto results = index.Search({}); REQUIRE(results.Matches.size() == 1); @@ -3014,7 +3017,7 @@ TEST_CASE("SQLiteIndex_ManifestArpVersion_Present_AddThenUpdate", "[sqliteindex] index.UpdateManifest(manifest, "path"); - Schema::Version testVersion = TestPrepareForRead(index); + SQLiteVersion testVersion = TestPrepareForRead(index); auto results = index.Search({}); REQUIRE(results.Matches.size() == 1); @@ -3048,7 +3051,7 @@ TEST_CASE("SQLiteIndex_ManifestArpVersion_Empty", "[sqliteindex]") manifest.Version = "Bar"; index.AddManifest(manifest, "path"); - Schema::Version testVersion = TestPrepareForRead(index); + SQLiteVersion testVersion = TestPrepareForRead(index); auto results = index.Search({}); REQUIRE(results.Matches.size() == 1); @@ -3075,7 +3078,7 @@ TEST_CASE("SQLiteIndex_RemoveManifestArpVersionKeepUsedDeleteUnused", "[sqlitein TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); - SQLiteIndex index = CreateTestIndex(tempFile, Schema::Version::Latest()); + SQLiteIndex index = CreateTestIndex(tempFile, SQLiteVersion::Latest()); Manifest manifest; manifest.Id = "Foo"; @@ -3123,7 +3126,7 @@ TEST_CASE("SQLiteIndex_ManifestArpVersion_CheckConsistency", "[sqliteindex]") TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); - SQLiteIndex index = CreateTestIndex(tempFile, Schema::Version::Latest()); + SQLiteIndex index = CreateTestIndex(tempFile, SQLiteVersion::Latest()); Manifest manifest; manifest.Id = "Foo"; @@ -3154,7 +3157,7 @@ TEST_CASE("SQLiteIndex_ManifestArpVersion_ValidateManifestAgainstIndex", "[sqlit TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); - SQLiteIndex index = CreateTestIndex(tempFile, Schema::Version::Latest()); + SQLiteIndex index = CreateTestIndex(tempFile, SQLiteVersion::Latest()); Manifest manifest; manifest.Id = "Foo"; @@ -3181,7 +3184,7 @@ TEST_CASE("SQLiteIndex_CheckConsistency_FindEmbeddedNull", "[sqliteindex]") TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); - SQLiteIndex index = CreateTestIndex(tempFile, Schema::Version::Latest()); + SQLiteIndex index = CreateTestIndex(tempFile, SQLiteVersion::Latest()); Manifest manifest; manifest.Id = "Foo"; @@ -3219,7 +3222,7 @@ TEST_CASE("SQLiteIndex_MapDataFolding_Tags", "[sqliteindex][mapdatafolding]") // Apply the map data folding if it is present in the created test index. index.PrepareForPackaging(); - Schema::Version testVersion = TestPrepareForRead(index); + SQLiteVersion testVersion = TestPrepareForRead(index); SearchRequest request1; request1.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::Tag, MatchType::Exact, tag1)); @@ -3250,7 +3253,7 @@ TEST_CASE("SQLiteIndex_MapDataFolding_PFNs", "[sqliteindex][mapdatafolding]") // Apply the map data folding if it is present in the created test index. index.PrepareForPackaging(); - Schema::Version testVersion = TestPrepareForRead(index); + SQLiteVersion testVersion = TestPrepareForRead(index); SearchRequest request1; request1.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::PackageFamilyName, MatchType::Exact, pfn1)); @@ -3321,7 +3324,7 @@ TEST_CASE("SQLiteIndex_MapDataFolding_ProductCodes", "[sqliteindex][mapdatafoldi // Apply the map data folding if it is present in the created test index. index.PrepareForPackaging(); - Schema::Version testVersion = TestPrepareForRead(index); + SQLiteVersion testVersion = TestPrepareForRead(index); SearchRequest request1; request1.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::ProductCode, MatchType::Exact, pc1)); diff --git a/src/AppInstallerCLITests/SQLiteIndexSource.cpp b/src/AppInstallerCLITests/SQLiteIndexSource.cpp @@ -10,11 +10,11 @@ using namespace TestCommon; using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; using namespace AppInstaller::Repository::Microsoft; -using namespace AppInstaller::Repository::SQLite; +using namespace AppInstaller::SQLite; static std::shared_ptr<SQLiteIndexSource> SimpleTestSetup(const std::string& filePath, SourceDetails& details, Manifest& manifest, std::string& relativePath) { - SQLiteIndex index = SQLiteIndex::CreateNew(filePath, Schema::Version::Latest()); + SQLiteIndex index = SQLiteIndex::CreateNew(filePath, Version::Latest()); TestDataFile testManifest("Manifest-Good.yaml"); manifest = YamlParser::CreateFromPath(testManifest); diff --git a/src/AppInstallerCLITests/SQLiteWrapper.cpp b/src/AppInstallerCLITests/SQLiteWrapper.cpp @@ -3,10 +3,10 @@ #include "pch.h" #include "TestCommon.h" #include <AppInstallerErrors.h> -#include <SQLiteWrapper.h> -#include <SQLiteStatementBuilder.h> +#include <winget/SQLiteWrapper.h> +#include <winget/SQLiteStatementBuilder.h> -using namespace AppInstaller::Repository::SQLite; +using namespace AppInstaller::SQLite; using namespace std::string_literals; static const char* s_firstColumn = "first"; diff --git a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj @@ -412,7 +412,6 @@ <ClInclude Include="Public\winget\ExperimentalFeature.h" /> <ClInclude Include="Public\winget\ExtensionCatalog.h" /> <ClInclude Include="Public\winget\Locale.h" /> - <ClInclude Include="Public\winget\ManagedFile.h" /> <ClInclude Include="Public\winget\Manifest.h" /> <ClInclude Include="Public\winget\ManifestInstaller.h" /> <ClInclude Include="Public\winget\ManifestLocalization.h" /> @@ -466,7 +465,6 @@ <ExcludedFromBuild Condition="'$(Configuration)'=='Fuzzing'">true</ExcludedFromBuild> </ClCompile> <ClCompile Include="Locale.cpp" /> - <ClCompile Include="ManagedFile.cpp" /> <ClCompile Include="Manifest\Manifest.cpp" /> <ClCompile Include="Manifest\ManifestCommon.cpp" /> <ClCompile Include="Manifest\ManifestValidation.cpp" /> diff --git a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj.filters b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj.filters @@ -150,9 +150,6 @@ <ClInclude Include="Public\winget\PortableFileEntry.h"> <Filter>Public\winget</Filter> </ClInclude> - <ClInclude Include="Public\winget\ManagedFile.h"> - <Filter>Public\winget</Filter> - </ClInclude> <ClInclude Include="Public\winget\MsixManifest.h"> <Filter>Header Files</Filter> </ClInclude> @@ -290,9 +287,6 @@ <ClCompile Include="PortableARPEntry.cpp"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="ManagedFile.cpp"> - <Filter>Source Files</Filter> - </ClCompile> <ClCompile Include="MsixManifest.cpp"> <Filter>Source Files</Filter> </ClCompile> diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj @@ -345,7 +345,6 @@ <ClInclude Include="ArpVersionValidation.h" /> <ClInclude Include="CompositeSource.h" /> <ClInclude Include="IconDefs.h" /> - <ClInclude Include="ICU\SQLiteICU.h" /> <ClInclude Include="ISource.h" /> <ClInclude Include="Microsoft\ARPHelper.h" /> <ClInclude Include="Microsoft\PinningIndex.h" /> @@ -395,14 +394,11 @@ <ClInclude Include="Microsoft\Schema\Pinning_1_0\PinTable.h" /> <ClInclude Include="Microsoft\Schema\Portable_1_0\PortableIndexInterface.h" /> <ClInclude Include="Microsoft\Schema\Portable_1_0\PortableTable.h" /> - <ClInclude Include="Microsoft\Schema\MetadataTable.h" /> <ClInclude Include="Microsoft\Schema\Checkpoint_1_0\CheckpointDataTable.h" /> <ClInclude Include="Microsoft\Schema\Checkpoint_1_0\CheckpointDatabaseInterface.h" /> <ClInclude Include="Microsoft\Schema\Checkpoint_1_0\CheckpointTable.h" /> - <ClInclude Include="Microsoft\Schema\Version.h" /> <ClInclude Include="Microsoft\SQLiteIndex.h" /> <ClInclude Include="Microsoft\SQLiteIndexSource.h" /> - <ClInclude Include="Microsoft\SQLiteStorageBase.h" /> <ClInclude Include="Microsoft\ConfigurableTestSourceFactory.h" /> <ClInclude Include="PackageDependenciesValidation.h" /> <ClInclude Include="PackageTrackingCatalogSourceFactory.h" /> @@ -445,9 +441,6 @@ <ClInclude Include="SourceList.h" /> <ClInclude Include="SourcePolicy.h" /> <ClInclude Include="SourceUpdateChecks.h" /> - <ClInclude Include="SQLiteStatementBuilder.h" /> - <ClInclude Include="SQLiteTempTable.h" /> - <ClInclude Include="SQLiteWrapper.h" /> </ItemGroup> <ItemGroup> <ClCompile Include="ARPCorrelation.cpp" /> @@ -455,20 +448,6 @@ <ClCompile Include="IconExtraction.cpp" /> <ClCompile Include="ArpVersionValidation.cpp" /> <ClCompile Include="CompositeSource.cpp" /> - <ClCompile Include="ICU\SQLiteICU.c"> - <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader> - <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader> - <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='ReleaseStatic|Win32'">NotUsing</PrecompiledHeader> - <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">NotUsing</PrecompiledHeader> - <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">NotUsing</PrecompiledHeader> - <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='ReleaseStatic|ARM64'">NotUsing</PrecompiledHeader> - <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">NotUsing</PrecompiledHeader> - <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">NotUsing</PrecompiledHeader> - <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='ReleaseStatic|ARM'">NotUsing</PrecompiledHeader> - <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader> - <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader> - <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='ReleaseStatic|x64'">NotUsing</PrecompiledHeader> - </ClCompile> <ClCompile Include="InstalledFilesCorrelation.cpp" /> <ClCompile Include="InstallerMetadataCollectionContext.cpp" /> <ClCompile Include="ManifestJSONParser.cpp" /> @@ -498,7 +477,6 @@ <ClCompile Include="Microsoft\Schema\1_6\Interface_1_6.cpp" /> <ClCompile Include="Microsoft\Schema\1_6\SearchResultsTable_1_6.cpp" /> <ClCompile Include="Microsoft\Schema\1_7\Interface_1_7.cpp" /> - <ClCompile Include="Microsoft\Schema\MetadataTable.cpp" /> <ClCompile Include="Microsoft\Schema\Pinning_1_0\PinningIndexInterface_1_0.cpp" /> <ClCompile Include="Microsoft\Schema\Pinning_1_0\PinTable.cpp" /> <ClCompile Include="Microsoft\Schema\Portable_1_0\PortableIndexInterface_1_0.cpp" /> @@ -506,10 +484,8 @@ <ClCompile Include="Microsoft\Schema\Checkpoint_1_0\CheckpointDataTable.cpp" /> <ClCompile Include="Microsoft\Schema\Checkpoint_1_0\CheckpointDatabaseInterface_1_0.cpp" /> <ClCompile Include="Microsoft\Schema\Checkpoint_1_0\CheckpointTable.cpp" /> - <ClCompile Include="Microsoft\Schema\Version.cpp" /> <ClCompile Include="Microsoft\SQLiteIndex.cpp" /> <ClCompile Include="Microsoft\SQLiteIndexSource.cpp" /> - <ClCompile Include="Microsoft\SQLiteStorageBase.cpp" /> <ClCompile Include="PackageDependenciesValidation.cpp" /> <ClCompile Include="PackageInstalledStatus.cpp" /> <ClCompile Include="PackageTrackingCatalog.cpp" /> @@ -543,9 +519,6 @@ <ClCompile Include="SourceList.cpp" /> <ClCompile Include="SourcePolicy.cpp" /> <ClCompile Include="SourceUpdateChecks.cpp" /> - <ClCompile Include="SQLiteStatementBuilder.cpp" /> - <ClCompile Include="SQLiteTempTable.cpp" /> - <ClCompile Include="SQLiteWrapper.cpp" /> </ItemGroup> <ItemGroup> <None Include="Microsoft\README.md" /> diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters @@ -25,9 +25,6 @@ <Filter Include="Public"> <UniqueIdentifier>{69ce2e35-fe7f-41af-bd47-91a70131d167}</UniqueIdentifier> </Filter> - <Filter Include="ICU"> - <UniqueIdentifier>{dac1a359-45ac-4456-8a83-f7df10058197}</UniqueIdentifier> - </Filter> <Filter Include="Microsoft\Schema\1_1"> <UniqueIdentifier>{aef8989c-44fd-4848-ae2c-c81d3f2e2c72}</UniqueIdentifier> </Filter> @@ -102,18 +99,9 @@ <ClInclude Include="pch.h"> <Filter>Header Files</Filter> </ClInclude> - <ClInclude Include="SQLiteWrapper.h"> - <Filter>Header Files</Filter> - </ClInclude> <ClInclude Include="Microsoft\SQLiteIndex.h"> <Filter>Microsoft</Filter> </ClInclude> - <ClInclude Include="Microsoft\Schema\MetadataTable.h"> - <Filter>Microsoft\Schema</Filter> - </ClInclude> - <ClInclude Include="Microsoft\Schema\Version.h"> - <Filter>Microsoft\Schema</Filter> - </ClInclude> <ClInclude Include="Microsoft\Schema\ISQLiteIndex.h"> <Filter>Microsoft\Schema</Filter> </ClInclude> @@ -153,9 +141,6 @@ <ClInclude Include="Microsoft\Schema\1_0\PathPartTable.h"> <Filter>Microsoft\Schema\1_0</Filter> </ClInclude> - <ClInclude Include="SQLiteStatementBuilder.h"> - <Filter>Header Files</Filter> - </ClInclude> <ClInclude Include="SourceFactory.h"> <Filter>Header Files</Filter> </ClInclude> @@ -165,15 +150,9 @@ <ClInclude Include="Microsoft\SQLiteIndexSource.h"> <Filter>Microsoft</Filter> </ClInclude> - <ClInclude Include="SQLiteTempTable.h"> - <Filter>Header Files</Filter> - </ClInclude> <ClInclude Include="Microsoft\Schema\1_0\SearchResultsTable.h"> <Filter>Microsoft\Schema\1_0</Filter> </ClInclude> - <ClInclude Include="ICU\SQLiteICU.h"> - <Filter>ICU</Filter> - </ClInclude> <ClInclude Include="Microsoft\Schema\1_1\Interface.h"> <Filter>Microsoft\Schema\1_1</Filter> </ClInclude> @@ -342,9 +321,6 @@ <ClInclude Include="Microsoft\Schema\IPortableIndex.h"> <Filter>Microsoft\Schema</Filter> </ClInclude> - <ClInclude Include="Microsoft\SQLiteStorageBase.h"> - <Filter>Microsoft</Filter> - </ClInclude> <ClInclude Include="Rest\Schema\1_4\Json\ManifestDeserializer.h"> <Filter>Rest\Schema\1_4\Json</Filter> </ClInclude> @@ -422,18 +398,9 @@ <ClCompile Include="pch.cpp"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="SQLiteWrapper.cpp"> - <Filter>Source Files</Filter> - </ClCompile> <ClCompile Include="Microsoft\SQLiteIndex.cpp"> <Filter>Microsoft</Filter> </ClCompile> - <ClCompile Include="Microsoft\Schema\MetadataTable.cpp"> - <Filter>Microsoft\Schema</Filter> - </ClCompile> - <ClCompile Include="Microsoft\Schema\Version.cpp"> - <Filter>Microsoft\Schema</Filter> - </ClCompile> <ClCompile Include="Microsoft\Schema\1_0\OneToOneTable.cpp"> <Filter>Microsoft\Schema\1_0</Filter> </ClCompile> @@ -446,9 +413,6 @@ <ClCompile Include="Microsoft\Schema\1_0\PathPartTable.cpp"> <Filter>Microsoft\Schema\1_0</Filter> </ClCompile> - <ClCompile Include="SQLiteStatementBuilder.cpp"> - <Filter>Source Files</Filter> - </ClCompile> <ClCompile Include="RepositorySource.cpp"> <Filter>Source Files</Filter> </ClCompile> @@ -458,12 +422,6 @@ <ClCompile Include="Microsoft\SQLiteIndexSource.cpp"> <Filter>Microsoft</Filter> </ClCompile> - <ClCompile Include="SQLiteTempTable.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="ICU\SQLiteICU.c"> - <Filter>ICU</Filter> - </ClCompile> <ClCompile Include="Microsoft\Schema\1_0\Interface_1_0.cpp"> <Filter>Microsoft\Schema\1_0</Filter> </ClCompile> @@ -596,9 +554,6 @@ <ClCompile Include="Microsoft\Schema\Portable_1_0\PortableTable.cpp"> <Filter>Microsoft\Schema\Portable_1_0</Filter> </ClCompile> - <ClCompile Include="Microsoft\SQLiteStorageBase.cpp"> - <Filter>Microsoft</Filter> - </ClCompile> <ClCompile Include="Rest\Schema\1_4\Json\ManifestDeserializer_1_4.cpp"> <Filter>Rest\Schema\1_4\Json</Filter> </ClCompile> @@ -626,9 +581,6 @@ <ClCompile Include="Microsoft\PinningIndex.cpp"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="Microsoft\Schema\Pinning_1_0\PinningIndexInterface_1_0.cpp"> - <Filter>Source Files</Filter> - </ClCompile> <ClCompile Include="Microsoft\Schema\Pinning_1_0\PinTable.cpp"> <Filter>Microsoft\Schema\Pinning_1_0</Filter> </ClCompile> @@ -652,9 +604,6 @@ </ClCompile> <ClCompile Include="Microsoft\Schema\Checkpoint_1_0\CheckpointDataTable.cpp"> <Filter>Microsoft\Schema\Checkpoint_1_0</Filter> - </ClCompile> - <ClCompile Include="Microsoft\Schema\Checkpoint_1_0\CheckpointDatabaseInterface_1_0.cpp"> - <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="Microsoft\Schema\Checkpoint_1_0\CheckpointTable.cpp"> <Filter>Microsoft\Schema\Checkpoint_1_0</Filter> @@ -662,6 +611,12 @@ <ClCompile Include="SourceUpdateChecks.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="Microsoft\Schema\Checkpoint_1_0\CheckpointDatabaseInterface_1_0.cpp"> + <Filter>Microsoft\Schema\Checkpoint_1_0</Filter> + </ClCompile> + <ClCompile Include="Microsoft\Schema\Pinning_1_0\PinningIndexInterface_1_0.cpp"> + <Filter>Microsoft\Schema\Pinning_1_0</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <None Include="PropertySheet.props" /> diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointDatabase.cpp b/src/AppInstallerRepositoryCore/Microsoft/CheckpointDatabase.cpp @@ -6,7 +6,7 @@ namespace AppInstaller::Repository::Microsoft { - std::shared_ptr<CheckpointDatabase> CheckpointDatabase::CreateNew(const std::string& filePath, Schema::Version version) + std::shared_ptr<CheckpointDatabase> CheckpointDatabase::CreateNew(const std::string& filePath, SQLite::Version version) { AICLI_LOG(Repo, Info, << "Creating new Checkpoint database with version [" << version << "] at '" << filePath << "'"); CheckpointDatabase result{ filePath, version }; @@ -103,7 +103,7 @@ namespace AppInstaller::Repository::Microsoft std::unique_ptr<Schema::ICheckpointDatabase> CheckpointDatabase::CreateICheckpointDatabase() const { - if (m_version == Schema::Version{ 1, 0 } || + if (m_version == SQLite::Version{ 1, 0 } || m_version.MajorVersion == 1 || m_version.IsLatest()) { @@ -121,7 +121,7 @@ namespace AppInstaller::Repository::Microsoft THROW_HR_IF(APPINSTALLER_CLI_ERROR_CANNOT_WRITE_TO_UPLEVEL_INDEX, disposition == SQLiteStorageBase::OpenDisposition::ReadWrite && m_version != m_interface->GetVersion()); } - CheckpointDatabase::CheckpointDatabase(const std::string& target, Schema::Version version) : SQLiteStorageBase(target, version) + CheckpointDatabase::CheckpointDatabase(const std::string& target, SQLite::Version version) : SQLiteStorageBase(target, version) { m_interface = CreateICheckpointDatabase(); m_version = m_interface->GetVersion(); diff --git a/src/AppInstallerRepositoryCore/Microsoft/CheckpointDatabase.h b/src/AppInstallerRepositoryCore/Microsoft/CheckpointDatabase.h @@ -1,14 +1,14 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once -#include "SQLiteWrapper.h" +#include <winget/SQLiteWrapper.h> #include "Microsoft/Schema/ICheckpointDatabase.h" -#include "Microsoft/SQLiteStorageBase.h" +#include <winget/SQLiteStorageBase.h> #include <winget/ManagedFile.h> namespace AppInstaller::Repository::Microsoft { - struct CheckpointDatabase : SQLiteStorageBase + struct CheckpointDatabase : SQLite::SQLiteStorageBase { // An id that refers to a specific Checkpoint. using IdType = SQLite::rowid_t; @@ -20,7 +20,7 @@ namespace AppInstaller::Repository::Microsoft CheckpointDatabase& operator=(CheckpointDatabase&&) = default; // Create a new checkpoint database. - static std::shared_ptr<CheckpointDatabase> CreateNew(const std::string& filePath, Schema::Version version = Schema::Version::Latest()); + static std::shared_ptr<CheckpointDatabase> CreateNew(const std::string& filePath, SQLite::Version version = SQLite::Version::Latest()); // Opens an existing checkpoint database. static std::shared_ptr<CheckpointDatabase> Open(const std::string& filePath, OpenDisposition disposition = OpenDisposition::ReadWrite, Utility::ManagedFile&& indexFile = {}) @@ -61,7 +61,7 @@ namespace AppInstaller::Repository::Microsoft CheckpointDatabase(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile); // Constructor used to create a new index. - CheckpointDatabase(const std::string& target, Schema::Version version); + CheckpointDatabase(const std::string& target, SQLite::Version version); // Creates the ICheckpointDatabase interface object for this version. std::unique_ptr<Schema::ICheckpointDatabase> CreateICheckpointDatabase() const; diff --git a/src/AppInstallerRepositoryCore/Microsoft/PinningIndex.cpp b/src/AppInstallerRepositoryCore/Microsoft/PinningIndex.cpp @@ -2,12 +2,12 @@ // Licensed under the MIT License. #include "pch.h" #include "PinningIndex.h" -#include "SQLiteStorageBase.h" +#include <winget/SQLiteStorageBase.h> #include "Schema/Pinning_1_0/PinningIndexInterface.h" namespace AppInstaller::Repository::Microsoft { - PinningIndex PinningIndex::CreateNew(const std::string& filePath, Schema::Version version) + PinningIndex PinningIndex::CreateNew(const std::string& filePath, SQLite::Version version) { AICLI_LOG(Repo, Info, << "Creating new Pinning Index with version [" << version << "] at '" << filePath << "'"); PinningIndex result{ filePath, version }; @@ -152,7 +152,7 @@ namespace AppInstaller::Repository::Microsoft std::unique_ptr<Schema::IPinningIndex> PinningIndex::CreateIPinningIndex() const { - if (m_version == Schema::Version{ 1, 0 } || + if (m_version == SQLite::Version{ 1, 0 } || m_version.MajorVersion == 1 || m_version.IsLatest()) { @@ -170,7 +170,7 @@ namespace AppInstaller::Repository::Microsoft THROW_HR_IF(APPINSTALLER_CLI_ERROR_CANNOT_WRITE_TO_UPLEVEL_INDEX, disposition == SQLiteStorageBase::OpenDisposition::ReadWrite && m_version != m_interface->GetVersion()); } - PinningIndex::PinningIndex(const std::string& target, Schema::Version version) : SQLiteStorageBase(target, version) + PinningIndex::PinningIndex(const std::string& target, SQLite::Version version) : SQLiteStorageBase(target, version) { m_interface = CreateIPinningIndex(); m_version = m_interface->GetVersion(); diff --git a/src/AppInstallerRepositoryCore/Microsoft/PinningIndex.h b/src/AppInstallerRepositoryCore/Microsoft/PinningIndex.h @@ -1,15 +1,15 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once -#include "SQLiteWrapper.h" +#include <winget/SQLiteWrapper.h> #include "Microsoft/Schema/IPinningIndex.h" -#include "Microsoft/SQLiteStorageBase.h" +#include <winget/SQLiteStorageBase.h> #include "winget/Pin.h" #include <winget/ManagedFile.h> namespace AppInstaller::Repository::Microsoft { - struct PinningIndex : SQLiteStorageBase + struct PinningIndex : SQLite::SQLiteStorageBase { // An id that refers to a specific Pinning file. using IdType = SQLite::rowid_t; @@ -21,7 +21,7 @@ namespace AppInstaller::Repository::Microsoft PinningIndex& operator=(PinningIndex&&) = default; // Creates a new PinningIndex database of the given version. - static PinningIndex CreateNew(const std::string& filePath, Schema::Version version = Schema::Version::Latest()); + static PinningIndex CreateNew(const std::string& filePath, SQLite::Version version = SQLite::Version::Latest()); // Opens an existing PinningIndex database. static PinningIndex Open(const std::string& filePath, OpenDisposition disposition, Utility::ManagedFile&& indexFile = {}) @@ -61,7 +61,7 @@ namespace AppInstaller::Repository::Microsoft PinningIndex(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile); // Constructor used to create a new index. - PinningIndex(const std::string& target, Schema::Version version); + PinningIndex(const std::string& target, SQLite::Version version); // Creates the IPinningIndex interface object for this version. std::unique_ptr<Schema::IPinningIndex> CreateIPinningIndex() const; diff --git a/src/AppInstallerRepositoryCore/Microsoft/PortableIndex.cpp b/src/AppInstallerRepositoryCore/Microsoft/PortableIndex.cpp @@ -2,13 +2,13 @@ // Licensed under the MIT License. #include "pch.h" #include "PortableIndex.h" -#include "SQLiteStorageBase.h" +#include <winget/SQLiteStorageBase.h> #include "Schema/Portable_1_0/PortableIndexInterface.h" #include "winget/Filesystem.h" namespace AppInstaller::Repository::Microsoft { - PortableIndex PortableIndex::CreateNew(const std::string& filePath, Schema::Version version) + PortableIndex PortableIndex::CreateNew(const std::string& filePath, SQLite::Version version) { AICLI_LOG(Repo, Info, << "Creating new Portable Index with version [" << version << "] at '" << filePath << "'"); PortableIndex result{ filePath, version }; @@ -108,7 +108,7 @@ namespace AppInstaller::Repository::Microsoft std::unique_ptr<Schema::IPortableIndex> PortableIndex::CreateIPortableIndex() const { - if (m_version == Schema::Version{ 1, 0 } || + if (m_version == SQLite::Version{ 1, 0 } || m_version.MajorVersion == 1 || m_version.IsLatest()) { @@ -126,7 +126,7 @@ namespace AppInstaller::Repository::Microsoft THROW_HR_IF(APPINSTALLER_CLI_ERROR_CANNOT_WRITE_TO_UPLEVEL_INDEX, disposition == SQLiteStorageBase::OpenDisposition::ReadWrite && m_version != m_interface->GetVersion()); } - PortableIndex::PortableIndex(const std::string& target, Schema::Version version) : SQLiteStorageBase(target, version) + PortableIndex::PortableIndex(const std::string& target, SQLite::Version version) : SQLiteStorageBase(target, version) { m_interface = CreateIPortableIndex(); m_version = m_interface->GetVersion(); diff --git a/src/AppInstallerRepositoryCore/Microsoft/PortableIndex.h b/src/AppInstallerRepositoryCore/Microsoft/PortableIndex.h @@ -1,10 +1,10 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once -#include "SQLiteWrapper.h" +#include <winget/SQLiteWrapper.h> #include "Microsoft/Schema/IPortableIndex.h" #include "Microsoft/Schema/Portable_1_0/PortableTable.h" -#include "Microsoft/SQLiteStorageBase.h" +#include <winget/SQLiteStorageBase.h> #include "winget/PortableFileEntry.h" #include <winget/ManagedFile.h> @@ -12,7 +12,7 @@ using namespace AppInstaller::Portable; namespace AppInstaller::Repository::Microsoft { - struct PortableIndex : SQLiteStorageBase + struct PortableIndex : SQLite::SQLiteStorageBase { // An id that refers to a specific portable file. using IdType = SQLite::rowid_t; @@ -24,7 +24,7 @@ namespace AppInstaller::Repository::Microsoft PortableIndex& operator=(PortableIndex&&) = default; // Creates a new PortableIndex database of the given version. - static PortableIndex CreateNew(const std::string& filePath, Schema::Version version = Schema::Version::Latest()); + static PortableIndex CreateNew(const std::string& filePath, SQLite::Version version = SQLite::Version::Latest()); // Opens an existing PortableIndex database. static PortableIndex Open(const std::string& filePath, OpenDisposition disposition, Utility::ManagedFile&& indexFile = {}) @@ -51,7 +51,7 @@ namespace AppInstaller::Repository::Microsoft PortableIndex(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile); // Constructor used to create a new index. - PortableIndex(const std::string& target, Schema::Version version); + PortableIndex(const std::string& target, SQLite::Version version); // Creates the IPortableIndex interface object for this version. std::unique_ptr<Schema::IPortableIndex> CreateIPortableIndex() const; diff --git a/src/AppInstallerRepositoryCore/Microsoft/PredefinedInstalledSourceFactory.cpp b/src/AppInstallerRepositoryCore/Microsoft/PredefinedInstalledSourceFactory.cpp @@ -130,7 +130,7 @@ namespace AppInstaller::Repository::Microsoft AICLI_LOG(Repo, Info, << "Creating PredefinedInstalledSource with filter [" << PredefinedInstalledSourceFactory::FilterToString(filter) << ']'); // Create an in memory index - SQLiteIndex index = SQLiteIndex::CreateNew(SQLITE_MEMORY_DB_CONNECTION_TARGET, Schema::Version::Latest()); + SQLiteIndex index = SQLiteIndex::CreateNew(SQLITE_MEMORY_DB_CONNECTION_TARGET, SQLite::Version::Latest()); // Put installed packages into the index if (filter == PredefinedInstalledSourceFactory::Filter::None || filter == PredefinedInstalledSourceFactory::Filter::ARP || diff --git a/src/AppInstallerRepositoryCore/Microsoft/PredefinedWriteableSourceFactory.cpp b/src/AppInstallerRepositoryCore/Microsoft/PredefinedWriteableSourceFactory.cpp @@ -66,7 +66,7 @@ namespace AppInstaller::Repository::Microsoft [&]() { // Create an in memory index without paths or dependencies - SQLiteIndex index = SQLiteIndex::CreateNew(SQLITE_MEMORY_DB_CONNECTION_TARGET, Schema::Version::Latest(), SQLiteIndex::CreateOptions::SupportPathless | SQLiteIndex::CreateOptions::DisableDependenciesSupport); + SQLiteIndex index = SQLiteIndex::CreateNew(SQLITE_MEMORY_DB_CONNECTION_TARGET, SQLite::Version::Latest(), SQLiteIndex::CreateOptions::SupportPathless | SQLiteIndex::CreateOptions::DisableDependenciesSupport); g_sharedSource = std::make_shared<SQLiteIndexWriteableSource>(m_details, std::move(index), true); }); diff --git a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndex.cpp b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndex.cpp @@ -2,7 +2,7 @@ // Licensed under the MIT License. #include "pch.h" #include "SQLiteIndex.h" -#include "SQLiteStorageBase.h" +#include <winget/SQLiteStorageBase.h> #include "ArpVersionValidation.h" #include <winget/ManifestYamlParser.h> @@ -17,7 +17,7 @@ namespace AppInstaller::Repository::Microsoft { - SQLiteIndex SQLiteIndex::CreateNew(const std::string& filePath, Schema::Version version, CreateOptions options) + SQLiteIndex SQLiteIndex::CreateNew(const std::string& filePath, SQLite::Version version, CreateOptions options) { AICLI_LOG(Repo, Info, << "Creating new SQLite Index with version [" << version << "] at '" << filePath << "'"); SQLiteIndex result{ filePath, version }; @@ -36,7 +36,7 @@ namespace AppInstaller::Repository::Microsoft return result; } - std::unique_ptr<Schema::ISQLiteIndex> SQLiteIndex::CreateISQLiteIndex(const Schema::Version& version) + std::unique_ptr<Schema::ISQLiteIndex> SQLiteIndex::CreateISQLiteIndex(const SQLite::Version& version) { using namespace Schema; @@ -62,7 +62,7 @@ namespace AppInstaller::Repository::Microsoft THROW_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); } - SQLiteIndex::SQLiteIndex(const std::string& target, const Schema::Version& version) : SQLiteStorageBase(target, version) + SQLiteIndex::SQLiteIndex(const std::string& target, const SQLite::Version& version) : SQLiteStorageBase(target, version) { m_dbconn.EnableICU(); m_interface = CreateISQLiteIndex(version); @@ -79,14 +79,14 @@ namespace AppInstaller::Repository::Microsoft } #ifndef AICLI_DISABLE_TEST_HOOKS - void SQLiteIndex::ForceVersion(const Schema::Version& version) + void SQLiteIndex::ForceVersion(const SQLite::Version& version) { m_interface = CreateISQLiteIndex(version); } - Schema::Version SQLiteIndex::GetLatestVersion() + SQLite::Version SQLiteIndex::GetLatestVersion() { - return CreateISQLiteIndex(Schema::Version::Latest())->GetVersion(); + return CreateISQLiteIndex(SQLite::Version::Latest())->GetVersion(); } #endif diff --git a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndex.h b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndex.h @@ -1,10 +1,10 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once -#include "SQLiteWrapper.h" +#include <winget/SQLiteWrapper.h> #include "Microsoft/Schema/ISQLiteIndex.h" -#include "Microsoft/Schema/Version.h" -#include "Microsoft/SQLiteStorageBase.h" +#include <winget/SQLiteVersion.h> +#include <winget/SQLiteStorageBase.h> #include "ISource.h" #include <AppInstallerLanguageUtilities.h> #include <AppInstallerVersions.h> @@ -23,7 +23,7 @@ namespace AppInstaller::Repository::Microsoft { // Holds the connection to the database, as well as the appropriate functionality to interface with it. - struct SQLiteIndex : SQLiteStorageBase + struct SQLiteIndex : SQLite::SQLiteStorageBase { // An id that refers to a specific application. using IdType = SQLite::rowid_t; @@ -44,7 +44,7 @@ namespace AppInstaller::Repository::Microsoft SQLiteIndex& operator=(SQLiteIndex&&) = default; // Creates a new index database of the given version. - static SQLiteIndex CreateNew(const std::string& filePath, Schema::Version version = Schema::Version::Latest(), CreateOptions options = CreateOptions::None); + static SQLiteIndex CreateNew(const std::string& filePath, SQLite::Version version = SQLite::Version::Latest(), CreateOptions options = CreateOptions::None); // Opens an existing SQLiteIndex database. static SQLiteIndex Open(const std::string& filePath, OpenDisposition disposition, Utility::ManagedFile&& indexFile = {}) @@ -55,10 +55,10 @@ namespace AppInstaller::Repository::Microsoft #ifndef AICLI_DISABLE_TEST_HOOKS // Changes the version of the interface being used to operate on the database. // Should only be used for testing. - void ForceVersion(const Schema::Version& version); + void ForceVersion(const SQLite::Version& version); // Gets the latest version of the index schema (the actual numbers, not just the latest sentinel values). - static Schema::Version GetLatestVersion(); + static SQLite::Version GetLatestVersion(); #endif // Adds the manifest at the repository relative path to the index. @@ -141,7 +141,7 @@ namespace AppInstaller::Repository::Microsoft std::vector<std::pair<SQLite::rowid_t, Utility::NormalizedString>> GetDependentsById(AppInstaller::Manifest::string_t packageId) const; private: // Constructor used to create a new index. - SQLiteIndex(const std::string& target, const Schema::Version& version); + SQLiteIndex(const std::string& target, const SQLite::Version& version); // Constructor used to open an existing index. SQLiteIndex(const std::string& target, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile); @@ -151,7 +151,7 @@ namespace AppInstaller::Repository::Microsoft bool UpdateManifestInternal(const Manifest::Manifest& manifest, const std::optional<std::filesystem::path>& relativePath); // Creates the ISQLiteIndex interface object for this version. - static std::unique_ptr<Schema::ISQLiteIndex> CreateISQLiteIndex(const Schema::Version& version); + static std::unique_ptr<Schema::ISQLiteIndex> CreateISQLiteIndex(const SQLite::Version& version); std::unique_ptr<Schema::ISQLiteIndex> m_interface; }; diff --git a/src/AppInstallerRepositoryCore/Microsoft/SQLiteStorageBase.cpp b/src/AppInstallerRepositoryCore/Microsoft/SQLiteStorageBase.cpp @@ -1,114 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#include "pch.h" -#include "SQLiteStorageBase.h" -#include "Schema/MetadataTable.h" - -namespace AppInstaller::Repository::Microsoft -{ - namespace - { - static char const* const GetOpenDispositionString(SQLiteStorageBase::OpenDisposition disposition) - { - switch (disposition) - { - case AppInstaller::Repository::Microsoft::SQLiteStorageBase::OpenDisposition::Read: - return "Read"; - case AppInstaller::Repository::Microsoft::SQLiteStorageBase::OpenDisposition::ReadWrite: - return "ReadWrite"; - case AppInstaller::Repository::Microsoft::SQLiteStorageBase::OpenDisposition::Immutable: - return "ImmutableRead"; - default: - return "Unknown"; - } - } - } - - // One method for converting open disposition to proper open disposition - // another method for obtaining the right flags - void SQLiteStorageBase::SetLastWriteTime() - { - Schema::MetadataTable::SetNamedValue(m_dbconn, Schema::s_MetadataValueName_LastWriteTime, Utility::GetCurrentUnixEpoch()); - } - - // Recording last write time based on MSDN documentation stating that time returns a POSIX epoch time and thus - // should be consistent across systems. - std::chrono::system_clock::time_point SQLiteStorageBase::GetLastWriteTime() - { - int64_t lastWriteTime = Schema::MetadataTable::GetNamedValue<int64_t>(m_dbconn, Schema::s_MetadataValueName_LastWriteTime); - return Utility::ConvertUnixEpochToSystemClock(lastWriteTime); - } - - SQLiteStorageBase::SQLiteStorageBase(const std::string& filePath, OpenDisposition disposition, Utility::ManagedFile&& indexFile) : - m_indexFile(std::move(indexFile)) - { - AICLI_LOG(Repo, Info, << "Opening SQLite Index for " << GetOpenDispositionString(disposition) << " at '" << filePath << "'"); - switch (disposition) - { - case OpenDisposition::Read: - m_dbconn = SQLite::Connection::Create(filePath, SQLite::Connection::OpenDisposition::ReadOnly, SQLite::Connection::OpenFlags::None); - break; - case OpenDisposition::ReadWrite: - m_dbconn = SQLite::Connection::Create(filePath, SQLite::Connection::OpenDisposition::ReadWrite, SQLite::Connection::OpenFlags::None); - break; - case OpenDisposition::Immutable: - { - // Following the algorithm set forth at https://sqlite.org/uri.html [3.1] to convert to a URI path - // The execution order builds out the string so that it shouldn't require any moves (other than growing) - std::string target; - // Add an 'arbitrary' growth size to prevent the majority of needing to grow (adding 'file:/' and '?immutable=1') - target.reserve(filePath.size() + 20); - - target += "file:"; - - bool wasLastCharSlash = false; - - if (filePath.size() >= 2 && filePath[1] == ':' && - ((filePath[0] >= 'a' && filePath[0] <= 'z') || - (filePath[0] >= 'A' && filePath[0] <= 'Z'))) - { - target += '/'; - wasLastCharSlash = true; - } - - for (char c : filePath) - { - bool wasThisCharSlash = false; - switch (c) - { - case '?': target += "%3f"; break; - case '#': target += "%23"; break; - case '\\': - case '/': - { - wasThisCharSlash = true; - if (!wasLastCharSlash) - { - target += '/'; - } - break; - } - default: target += c; break; - } - - wasLastCharSlash = wasThisCharSlash; - } - - target += "?immutable=1"; - m_dbconn = SQLite::Connection::Create(filePath, SQLite::Connection::OpenDisposition::ReadOnly, SQLite::Connection::OpenFlags::Uri); - break; - } - default: - THROW_HR(E_UNEXPECTED); - } - - m_version = Schema::Version::GetSchemaVersion(m_dbconn); - } - - SQLiteStorageBase::SQLiteStorageBase(const std::string& target, const Schema::Version& version) : - m_dbconn(SQLite::Connection::Create(target, SQLite::Connection::OpenDisposition::Create)) - { - m_version = version; - Schema::MetadataTable::Create(m_dbconn); - } -}- \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/SQLiteStorageBase.h b/src/AppInstallerRepositoryCore/Microsoft/SQLiteStorageBase.h @@ -1,48 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#pragma once -#include "SQLiteWrapper.h" -#include "Microsoft/Schema/Version.h" -#include <winget/ManagedFile.h> -#include <AppInstallerVersions.h> - -#include <mutex> - -namespace AppInstaller::Repository::Microsoft -{ - struct SQLiteStorageBase - { - // The disposition for opening the index. - enum class OpenDisposition - { - // Open for read only. - Read, - // Open for read and write. - ReadWrite, - // The database will not change while in use; open for immutable read. - Immutable, - }; - - // Gets the last write time for the index. - std::chrono::system_clock::time_point GetLastWriteTime(); - - // Gets the schema version of the index. - Schema::Version GetVersion() const { return m_version; } - - protected: - SQLiteStorageBase(const std::string& target, const Schema::Version& version); - - SQLiteStorageBase(const std::string& filePath, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile); - - // Sets the last write time metadata value in the index. - void SetLastWriteTime(); - - // Gets the corresponding OpenFlags based on the disposition. - SQLite::Connection::OpenFlags GetOpenFlags(SQLiteStorageBase::OpenDisposition disposition); - - Utility::ManagedFile m_indexFile; - SQLite::Connection m_dbconn; - Schema::Version m_version; - std::unique_ptr<std::mutex> m_interfaceLock = std::make_unique<std::mutex>(); - }; -}- \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/Interface.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/Interface.h @@ -15,7 +15,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 struct Interface : public ISQLiteIndex { // Version 1.0 - Schema::Version GetVersion() const override; + SQLite::Version GetVersion() const override; void CreateTables(SQLite::Connection& connection, CreateOptions options) override; SQLite::rowid_t AddManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional<std::filesystem::path>& relativePath) override; std::pair<bool, SQLite::rowid_t> UpdateManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional<std::filesystem::path>& relativePath) override; diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/Interface_1_0.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/Interface_1_0.cpp @@ -150,7 +150,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 } } - Schema::Version Interface::GetVersion() const + SQLite::Version Interface::GetVersion() const { return { 1, 0 }; } diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ManifestTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ManifestTable.cpp @@ -2,7 +2,7 @@ // Licensed under the MIT License. #include "pch.h" #include "ManifestTable.h" -#include "SQLiteStatementBuilder.h" +#include <winget/SQLiteStatementBuilder.h> #include "OneToManyTable.h" diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ManifestTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ManifestTable.h @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once -#include "SQLiteWrapper.h" -#include "SQLiteStatementBuilder.h" +#include <winget/SQLiteWrapper.h> +#include <winget/SQLiteStatementBuilder.h> #include "Microsoft/Schema/1_0/VirtualTableBase.h" #include <initializer_list> #include <optional> diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToManyTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToManyTable.cpp @@ -5,7 +5,7 @@ #include "Microsoft/Schema/1_0/OneToOneTable.h" #include "Microsoft/Schema/1_0/ManifestTable.h" #include "Microsoft/Schema/1_0/IdTable.h" -#include "SQLiteStatementBuilder.h" +#include <winget/SQLiteStatementBuilder.h> namespace AppInstaller::Repository::Microsoft::Schema::V1_0 diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToManyTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToManyTable.h @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once -#include "SQLiteWrapper.h" +#include <winget/SQLiteWrapper.h> #include <string> #include <string_view> #include <vector> diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToOneTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToOneTable.cpp @@ -3,7 +3,7 @@ #include "pch.h" #include "Microsoft/Schema/1_0/OneToOneTable.h" #include "Microsoft/Schema/1_0/ManifestTable.h" -#include "SQLiteStatementBuilder.h" +#include <winget/SQLiteStatementBuilder.h> namespace AppInstaller::Repository::Microsoft::Schema::V1_0 diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToOneTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToOneTable.h @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once -#include "SQLiteWrapper.h" +#include <winget/SQLiteWrapper.h> #include <optional> #include <string> #include <string_view> diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/PathPartTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/PathPartTable.cpp @@ -2,7 +2,7 @@ // Licensed under the MIT License. #include "pch.h" #include "PathPartTable.h" -#include "SQLiteStatementBuilder.h" +#include <winget/SQLiteStatementBuilder.h> namespace AppInstaller::Repository::Microsoft::Schema::V1_0 diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/PathPartTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/PathPartTable.h @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once -#include "SQLiteWrapper.h" +#include <winget/SQLiteWrapper.h> #include <filesystem> #include <optional> #include <string> diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/SearchResultsTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/SearchResultsTable.h @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once -#include "SQLiteWrapper.h" -#include "SQLiteTempTable.h" +#include <winget/SQLiteWrapper.h> +#include <winget/SQLiteTempTable.h> #include "Microsoft/Schema/ISQLiteIndex.h" #include "Public/winget/RepositorySearch.h" diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/SearchResultsTable_1_0.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/SearchResultsTable_1_0.cpp @@ -2,7 +2,7 @@ // Licensed under the MIT License. #include "pch.h" #include "SearchResultsTable.h" -#include "SQLiteStatementBuilder.h" +#include <winget/SQLiteStatementBuilder.h> #include "Microsoft/Schema/1_0/IdTable.h" #include "Microsoft/Schema/1_0/NameTable.h" diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/Interface.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/Interface.h @@ -11,7 +11,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_1 struct Interface : public V1_0::Interface { // Version 1.0 - Schema::Version GetVersion() const override; + SQLite::Version GetVersion() const override; void CreateTables(SQLite::Connection& connection, CreateOptions options) override; SQLite::rowid_t AddManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional<std::filesystem::path>& relativePath) override; std::pair<bool, SQLite::rowid_t> UpdateManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional<std::filesystem::path>& relativePath) override; diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/Interface_1_1.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/Interface_1_1.cpp @@ -25,7 +25,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_1 { - Schema::Version Interface::GetVersion() const + SQLite::Version Interface::GetVersion() const { return { 1, 1 }; } diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/ManifestMetadataTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/ManifestMetadataTable.cpp @@ -2,7 +2,7 @@ // Licensed under the MIT License. #include "pch.h" #include "ManifestMetadataTable.h" -#include "SQLiteStatementBuilder.h" +#include <winget/SQLiteStatementBuilder.h> namespace AppInstaller::Repository::Microsoft::Schema::V1_1 diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/ManifestMetadataTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/ManifestMetadataTable.h @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once -#include "SQLiteWrapper.h" +#include <winget/SQLiteWrapper.h> #include "Microsoft/Schema/ISQLiteIndex.h" #include "Public/winget/RepositorySearch.h" diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_2/Interface.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_2/Interface.h @@ -13,7 +13,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_2 Interface(Utility::NormalizationVersion normVersion = Utility::NormalizationVersion::Initial); // Version 1.0 - Schema::Version GetVersion() const override; + SQLite::Version GetVersion() const override; void CreateTables(SQLite::Connection& connection, CreateOptions options) override; SQLite::rowid_t AddManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional<std::filesystem::path>& relativePath) override; std::pair<bool, SQLite::rowid_t> UpdateManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional<std::filesystem::path>& relativePath) override; diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_2/Interface_1_2.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_2/Interface_1_2.cpp @@ -150,7 +150,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_2 { } - Schema::Version Interface::GetVersion() const + SQLite::Version Interface::GetVersion() const { return { 1, 2 }; } diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_3/HashVirtualTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_3/HashVirtualTable.h @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once -#include "SQLiteStatementBuilder.h" +#include <winget/SQLiteStatementBuilder.h> #include <string_view> diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_3/Interface.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_3/Interface.h @@ -13,7 +13,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_3 Interface(Utility::NormalizationVersion normVersion = Utility::NormalizationVersion::Initial); // Version 1.0 - Schema::Version GetVersion() const override; + SQLite::Version GetVersion() const override; void CreateTables(SQLite::Connection& connection, CreateOptions options) override; SQLite::rowid_t AddManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional<std::filesystem::path>& relativePath) override; std::pair<bool, SQLite::rowid_t> UpdateManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional<std::filesystem::path>& relativePath) override; diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_3/Interface_1_3.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_3/Interface_1_3.cpp @@ -14,7 +14,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_3 { } - Schema::Version Interface::GetVersion() const + SQLite::Version Interface::GetVersion() const { return { 1, 3 }; } diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_4/DependenciesTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_4/DependenciesTable.cpp @@ -2,7 +2,7 @@ // Licensed under the MIT License. #include "pch.h" #include "DependenciesTable.h" -#include "SQLiteStatementBuilder.h" +#include <winget/SQLiteStatementBuilder.h> #include "winget\DependenciesGraph.h" #include "Microsoft/Schema/1_0/OneToOneTable.h" #include "Microsoft/Schema/1_0/IdTable.h" diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_4/DependenciesTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_4/DependenciesTable.h @@ -2,8 +2,8 @@ // Licensed under the MIT License. #pragma once #include "pch.h" -#include "SQLiteWrapper.h" -#include "SQLiteStatementBuilder.h" +#include <winget/SQLiteWrapper.h> +#include <winget/SQLiteStatementBuilder.h> #include <winget/Manifest.h> namespace AppInstaller::Repository::Microsoft::Schema::V1_4 diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_4/Interface.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_4/Interface.h @@ -12,7 +12,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_4 Interface(Utility::NormalizationVersion normVersion = Utility::NormalizationVersion::Initial); // Version 1.0 - Schema::Version GetVersion() const override; + SQLite::Version GetVersion() const override; void CreateTables(SQLite::Connection& connection, CreateOptions options) override; SQLite::rowid_t AddManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional<std::filesystem::path>& relativePath) override; std::pair<bool, SQLite::rowid_t> UpdateManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional<std::filesystem::path>& relativePath) override; diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_4/Interface_1_4.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_4/Interface_1_4.cpp @@ -12,7 +12,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_4 { } - Schema::Version Interface::GetVersion() const + SQLite::Version Interface::GetVersion() const { return { 1, 4 }; } diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_5/ArpVersionVirtualTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_5/ArpVersionVirtualTable.h @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once -#include "SQLiteStatementBuilder.h" +#include <winget/SQLiteStatementBuilder.h> #include "Microsoft/Schema/1_0/VersionTable.h" #include "Microsoft/Schema/1_0/VirtualTableBase.h" #include <string_view> diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_5/Interface.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_5/Interface.h @@ -12,7 +12,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_5 Interface(Utility::NormalizationVersion normVersion = Utility::NormalizationVersion::Initial); // Version 1.0 - Schema::Version GetVersion() const override; + SQLite::Version GetVersion() const override; void CreateTables(SQLite::Connection& connection, CreateOptions options) override; SQLite::rowid_t AddManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional<std::filesystem::path>& relativePath) override; std::pair<bool, SQLite::rowid_t> UpdateManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional<std::filesystem::path>& relativePath) override; diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_5/Interface_1_5.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_5/Interface_1_5.cpp @@ -12,7 +12,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_5 { } - Schema::Version Interface::GetVersion() const + SQLite::Version Interface::GetVersion() const { return { 1, 5 }; } diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_6/Interface.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_6/Interface.h @@ -12,7 +12,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_6 Interface(Utility::NormalizationVersion normVersion = Utility::NormalizationVersion::Initial); // Version 1.0 - Schema::Version GetVersion() const override; + SQLite::Version GetVersion() const override; void CreateTables(SQLite::Connection& connection, CreateOptions options) override; SQLite::rowid_t AddManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional<std::filesystem::path>& relativePath) override; std::pair<bool, SQLite::rowid_t> UpdateManifest(SQLite::Connection& connection, const Manifest::Manifest& manifest, const std::optional<std::filesystem::path>& relativePath) override; diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_6/Interface_1_6.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_6/Interface_1_6.cpp @@ -13,7 +13,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_6 { } - Schema::Version Interface::GetVersion() const + SQLite::Version Interface::GetVersion() const { return { 1, 6 }; } diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_7/Interface.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_7/Interface.h @@ -8,6 +8,10 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_7 { using namespace std::string_view_literals; + // Version 1.7 + static constexpr std::string_view s_MetadataValueName_MapDataFolded = "mapDataFolded"sv; + static constexpr char s_MetadataValue_MapDataFolded_Separator = ';'; + // Interface to this schema version exposed through ISQLiteIndex. struct Interface : public V1_6::Interface { @@ -16,7 +20,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_7 Interface(Utility::NormalizationVersion normVersion = Utility::NormalizationVersion::Initial); // Version 1.0 - Schema::Version GetVersion() const override; + SQLite::Version GetVersion() const override; std::vector<std::string> GetMultiPropertyByManifestId(const SQLite::Connection& connection, SQLite::rowid_t manifestId, PackageVersionMultiProperty property) const override; protected: diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_7/Interface_1_7.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_7/Interface_1_7.cpp @@ -2,7 +2,7 @@ // Licensed under the MIT License. #include "pch.h" #include "Microsoft/Schema/1_7/Interface.h" -#include "Microsoft/Schema/MetadataTable.h" +#include <winget/SQLiteMetadataTable.h> #include "Microsoft/Schema/1_0/CommandsTable.h" #include "Microsoft/Schema/1_0/IdTable.h" #include "Microsoft/Schema/1_0/TagsTable.h" @@ -20,7 +20,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_7 // Get the metadata indicator that we folded these multi properties. // If it contains the value for folding these properties in the 1.7 manner, also fold the incoming manifest // to the same value that it would have been folded to so that all manifest entries will have all of these properties. - std::optional<std::string> mapDataFolded = MetadataTable::TryGetNamedValue<std::string>(connection, s_MetadataValueName_MapDataFolded); + std::optional<std::string> mapDataFolded = SQLite::MetadataTable::TryGetNamedValue<std::string>(connection, s_MetadataValueName_MapDataFolded); if (mapDataFolded) { @@ -43,7 +43,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_7 { } - Schema::Version Interface::GetVersion() const + SQLite::Version Interface::GetVersion() const { return { 1, 7 }; } @@ -95,7 +95,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_7 } } - MetadataTable::SetNamedValue(connection, s_MetadataValueName_MapDataFolded, MapDataFolded_VersionSpecifier); + SQLite::MetadataTable::SetNamedValue(connection, s_MetadataValueName_MapDataFolded, MapDataFolded_VersionSpecifier); V1_6::Interface::PrepareForPackaging(connection, false); diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDataTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDataTable.cpp @@ -2,7 +2,7 @@ // Licensed under the MIT License. #include "pch.h" #include "CheckpointDataTable.h" -#include "SQLiteStatementBuilder.h" +#include <winget/SQLiteStatementBuilder.h> namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDataTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDataTable.h @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once -#include "SQLiteWrapper.h" +#include <winget/SQLiteWrapper.h> #include <string_view> #include <vector> diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDatabaseInterface.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDatabaseInterface.h @@ -8,7 +8,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 struct CheckpointDatabaseInterface : public ICheckpointDatabase { // Version 1.0 - Schema::Version GetVersion() const override; + SQLite::Version GetVersion() const override; void CreateTables(SQLite::Connection& connection) override; private: diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDatabaseInterface_1_0.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointDatabaseInterface_1_0.cpp @@ -7,7 +7,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { - Schema::Version CheckpointDatabaseInterface::GetVersion() const + SQLite::Version CheckpointDatabaseInterface::GetVersion() const { return { 1, 0 }; } diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.cpp @@ -2,7 +2,7 @@ // Licensed under the MIT License. #include "pch.h" #include "CheckpointTable.h" -#include "SQLiteStatementBuilder.h" +#include <winget/SQLiteStatementBuilder.h> namespace AppInstaller::Repository::Microsoft::Schema::Checkpoint_V1_0 { diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Checkpoint_1_0/CheckpointTable.h @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once -#include "SQLiteWrapper.h" +#include <winget/SQLiteWrapper.h> #include <vector> #include <optional> diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointDatabase.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/ICheckpointDatabase.h @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once -#include "SQLiteWrapper.h" -#include "Microsoft/Schema/Version.h" +#include <winget/SQLiteWrapper.h> +#include <winget/SQLiteVersion.h> namespace AppInstaller::Repository::Microsoft::Schema { @@ -11,7 +11,7 @@ namespace AppInstaller::Repository::Microsoft::Schema virtual ~ICheckpointDatabase() = default; // Gets the schema version that this index interface is built for. - virtual Schema::Version GetVersion() const = 0; + virtual SQLite::Version GetVersion() const = 0; // Creates all of the version dependent tables within the database. virtual void CreateTables(SQLite::Connection& connection) = 0; diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/IPinningIndex.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/IPinningIndex.h @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once -#include "SQLiteWrapper.h" -#include "Microsoft/Schema/Version.h" +#include <winget/SQLiteWrapper.h> +#include <winget/SQLiteVersion.h> #include "winget/Pin.h" namespace AppInstaller::Repository::Microsoft::Schema @@ -12,7 +12,7 @@ namespace AppInstaller::Repository::Microsoft::Schema virtual ~IPinningIndex() = default; // Gets the schema version that this index interface is built for. - virtual Schema::Version GetVersion() const = 0; + virtual SQLite::Version GetVersion() const = 0; // Creates all of the version dependent tables within the database. virtual void CreateTables(SQLite::Connection& connection) = 0; diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/IPortableIndex.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/IPortableIndex.h @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once -#include "SQLiteWrapper.h" -#include "Microsoft/Schema/Version.h" +#include <winget/SQLiteWrapper.h> +#include <winget/SQLiteVersion.h> #include "winget/PortableFileEntry.h" #include <filesystem> @@ -13,7 +13,7 @@ namespace AppInstaller::Repository::Microsoft::Schema virtual ~IPortableIndex() = default; // Gets the schema version that this index interface is built for. - virtual Schema::Version GetVersion() const = 0; + virtual SQLite::Version GetVersion() const = 0; // Creates all of the version dependent tables within the database. virtual void CreateTable(SQLite::Connection& connection) = 0; diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/ISQLiteIndex.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/ISQLiteIndex.h @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once -#include "SQLiteWrapper.h" -#include "Microsoft/Schema/Version.h" +#include <winget/SQLiteWrapper.h> +#include <winget/SQLiteVersion.h> #include "ISource.h" #include <AppInstallerVersions.h> #include <winget/Manifest.h> @@ -14,9 +14,6 @@ namespace AppInstaller::Repository::Microsoft::Schema { - // Forward declarations - struct Version; - // The common interface used to interact with all schema versions of the index. struct ISQLiteIndex { @@ -36,7 +33,7 @@ namespace AppInstaller::Repository::Microsoft::Schema // Version 1.0 // Gets the schema version that this index interface is built for. - virtual Schema::Version GetVersion() const = 0; + virtual SQLite::Version GetVersion() const = 0; // Options for creating the index. enum class CreateOptions diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/MetadataTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/MetadataTable.cpp @@ -1,61 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#include "pch.h" -#include "MetadataTable.h" - - -namespace AppInstaller::Repository::Microsoft::Schema -{ - // Table data [note that this table is not versioned, and thus *cannot change*] - static constexpr std::string_view s_MetadataTable_Table_Name = "metadata"sv; - static constexpr std::string_view s_MetadataTable_Column_Name = "name"sv; - static constexpr std::string_view s_MetadataTable_Column_Value = "value"sv; - - static constexpr std::string_view s_MetadataTable_Table_Create = R"( -CREATE TABLE [metadata]( - [name] TEXT PRIMARY KEY NOT NULL, - [value] TEXT NOT NULL) -)"sv; - - // Statements - static constexpr std::string_view s_MetadataTableStmt_GetNamedValue = "select [value] from [metadata] where [name] = ?"sv; - static constexpr std::string_view s_MetadataTableStmt_SetNamedValue = "insert or replace into [metadata] ([name], [value]) values (?, ?)"sv; - - void MetadataTable::Create(SQLite::Connection& connection) - { - SQLite::Statement create = SQLite::Statement::Create(connection, s_MetadataTable_Table_Create); - create.Execute(); - } - - SQLite::Statement MetadataTable::GetNamedValueStatement(const SQLite::Connection& connection, std::string_view name) - { - std::optional<SQLite::Statement> result = TryGetNamedValueStatement(connection, name); - THROW_HR_IF(E_NOT_SET, !result); - return std::move(result).value(); - } - - std::optional<SQLite::Statement> MetadataTable::TryGetNamedValueStatement(const SQLite::Connection& connection, std::string_view name) - { - THROW_HR_IF(E_INVALIDARG, name.empty()); - - SQLite::Statement result = SQLite::Statement::Create(connection, s_MetadataTableStmt_GetNamedValue); - result.Bind(1, name); - - if (result.Step()) - { - return result; - } - else - { - return std::nullopt; - } - } - - SQLite::Statement MetadataTable::SetNamedValueStatement(const SQLite::Connection& connection, std::string_view name) - { - THROW_HR_IF(E_INVALIDARG, name.empty()); - SQLite::Statement result = SQLite::Statement::Create(connection, s_MetadataTableStmt_SetNamedValue); - result.Bind(1, name); - return result; - } -} diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/MetadataTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/MetadataTable.h @@ -1,72 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#pragma once -#include "SQLiteWrapper.h" - -#include <wil/result_macros.h> -#include <string_view> - -namespace AppInstaller::Repository::Microsoft::Schema -{ - using namespace std::string_view_literals; - // Named metadata values are defined here to reduce the risk of duplicate names. - - // Version 1.0 - static constexpr std::string_view s_MetadataValueName_MajorVersion = "majorVersion"sv; - static constexpr std::string_view s_MetadataValueName_MinorVersion = "minorVersion"sv; - static constexpr std::string_view s_MetadataValueName_LastWriteTime = "lastwritetime"sv; - - // Version 1.7 - static constexpr std::string_view s_MetadataValueName_MapDataFolded = "mapDataFolded"sv; - static constexpr char s_MetadataValue_MapDataFolded_Separator = ';'; - - // The metadata table for the index. - // Contains a fixed-schema set of named values that can be used to determine how to read the rest of the index. - struct MetadataTable - { - static void Create(SQLite::Connection& connection); - - // Gets the named value from the metadata table, interpreting it as the given type. - template <typename Value> - static Value GetNamedValue(const SQLite::Connection& connection, std::string_view name) - { - SQLite::Statement statement = GetNamedValueStatement(connection, name); - return statement.GetColumn<Value>(0); - } - - // Gets the named value from the metadata table, interpreting it as the given type. - // Returns nullopt if the value is not present. - template <typename Value> - static std::optional<Value> TryGetNamedValue(const SQLite::Connection& connection, std::string_view name) - { - std::optional<SQLite::Statement> statement = TryGetNamedValueStatement(connection, name); - if (statement) - { - return statement->GetColumn<Value>(0); - } - else - { - return std::nullopt; - } - } - - // Sets the named value into the metadata table. - template <typename Value> - static void SetNamedValue(const SQLite::Connection& connection, std::string_view name, Value&& v) - { - SQLite::Statement statement = SetNamedValueStatement(connection, name); - statement.Bind(2, std::forward<Value>(v)); - statement.Execute(); - } - - private: - // Internal function that gets the named value. - static SQLite::Statement GetNamedValueStatement(const SQLite::Connection& connection, std::string_view name); - - // Internal function that gets the named value, or nullopt if it is not present. - static std::optional<SQLite::Statement> TryGetNamedValueStatement(const SQLite::Connection& connection, std::string_view name); - - // Internal function that sets the named value. - static SQLite::Statement SetNamedValueStatement(const SQLite::Connection& connection, std::string_view name); - }; -} diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Pinning_1_0/PinTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Pinning_1_0/PinTable.cpp @@ -2,7 +2,7 @@ // Licensed under the MIT License. #include "pch.h" #include "PinTable.h" -#include "SQLiteStatementBuilder.h" +#include <winget/SQLiteStatementBuilder.h> #include "Microsoft/Schema/IPinningIndex.h" namespace AppInstaller::Repository::Microsoft::Schema::Pinning_V1_0 diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Pinning_1_0/PinTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Pinning_1_0/PinTable.h @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once -#include "SQLiteWrapper.h" -#include "SQLiteStatementBuilder.h" +#include <winget/SQLiteWrapper.h> +#include <winget/SQLiteStatementBuilder.h> #include "Microsoft/Schema/IPinningIndex.h" #include <string_view> diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Pinning_1_0/PinningIndexInterface.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Pinning_1_0/PinningIndexInterface.h @@ -8,7 +8,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::Pinning_V1_0 struct PinningIndexInterface : public IPinningIndex { // Version 1.0 - Schema::Version GetVersion() const override; + SQLite::Version GetVersion() const override; void CreateTables(SQLite::Connection& connection) override; private: diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Pinning_1_0/PinningIndexInterface_1_0.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Pinning_1_0/PinningIndexInterface_1_0.cpp @@ -23,7 +23,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::Pinning_V1_0 } // Version 1.0 - Schema::Version PinningIndexInterface::GetVersion() const + SQLite::Version PinningIndexInterface::GetVersion() const { return { 1, 0 }; } diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Portable_1_0/PortableIndexInterface.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Portable_1_0/PortableIndexInterface.h @@ -9,7 +9,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::Portable_V1_0 struct PortableIndexInterface : public IPortableIndex { // Version 1.0 - Schema::Version GetVersion() const override; + SQLite::Version GetVersion() const override; void CreateTable(SQLite::Connection& connection) override; private: diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Portable_1_0/PortableIndexInterface_1_0.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Portable_1_0/PortableIndexInterface_1_0.cpp @@ -21,7 +21,7 @@ namespace AppInstaller::Repository::Microsoft::Schema::Portable_V1_0 } } - Schema::Version PortableIndexInterface::GetVersion() const + SQLite::Version PortableIndexInterface::GetVersion() const { return { 1, 0 }; } diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Portable_1_0/PortableTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Portable_1_0/PortableTable.cpp @@ -2,7 +2,7 @@ // Licensed under the MIT License. #include "pch.h" #include "PortableTable.h" -#include "SQLiteStatementBuilder.h" +#include <winget/SQLiteStatementBuilder.h> #include "Microsoft/Schema/IPortableIndex.h" namespace AppInstaller::Repository::Microsoft::Schema::Portable_V1_0 diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Portable_1_0/PortableTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Portable_1_0/PortableTable.h @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once -#include "SQLiteWrapper.h" -#include "SQLiteStatementBuilder.h" +#include <winget/SQLiteWrapper.h> +#include <winget/SQLiteStatementBuilder.h> #include "Microsoft/Schema/IPortableIndex.h" #include <string_view> diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Version.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/Version.cpp @@ -1,64 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#include "pch.h" -#include "Version.h" -#include "MetadataTable.h" - -#include <limits> - -namespace AppInstaller::Repository::Microsoft::Schema -{ - Version Version::GetSchemaVersion(SQLite::Connection& connection) - { - int major = MetadataTable::GetNamedValue<int>(connection, Schema::s_MetadataValueName_MajorVersion); - int minor = MetadataTable::GetNamedValue<int>(connection, Schema::s_MetadataValueName_MinorVersion); - - return { static_cast<uint32_t>(major), static_cast<uint32_t>(minor) }; - } - - void Version::SetSchemaVersion(SQLite::Connection& connection) - { - SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "version_setschemaversion"); - - MetadataTable::SetNamedValue(connection, Schema::s_MetadataValueName_MajorVersion, static_cast<int>(MajorVersion)); - MetadataTable::SetNamedValue(connection, Schema::s_MetadataValueName_MinorVersion, static_cast<int>(MinorVersion)); - - savepoint.Commit(); - } - - std::ostream& operator<<(std::ostream& out, const Version& version) - { - if (version.IsLatest()) - { - return out << "Latest"; - } - else if (version.IsLatestForMajor(version.MajorVersion)) - { - return out << version.MajorVersion << ".Latest"; - } - else - { - return out << version.MajorVersion << '.' << version.MinorVersion; - } - } - - Version Version::Latest() - { - return { std::numeric_limits<uint32_t>::max(), std::numeric_limits<uint32_t>::max() }; - } - - Version Version::LatestForMajor(uint32_t majorVersion) - { - return { majorVersion, std::numeric_limits<uint32_t>::max() }; - } - - bool Version::IsLatest() const - { - return (MajorVersion == std::numeric_limits<uint32_t>::max() && MinorVersion == std::numeric_limits<uint32_t>::max()); - } - - bool Version::IsLatestForMajor(uint32_t majorVersion) const - { - return (MajorVersion == majorVersion && MinorVersion == std::numeric_limits<uint32_t>::max()); - } -} diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/Version.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/Version.h @@ -1,57 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#pragma once -#include "SQLiteWrapper.h" -#include <memory> - -namespace AppInstaller::Repository::Microsoft::Schema -{ - // Represents the schema version of the index. - struct Version - { - // The major version of the schema. - // All minor changes to this major version must be backward compatible. - uint32_t MajorVersion{}; - // The minor version of the schema. - // All changes to the schema warrant a change to the minor version. - uint32_t MinorVersion{}; - - bool operator==(const Version& other) const - { - return (MajorVersion == other.MajorVersion && MinorVersion == other.MinorVersion); - } - - bool operator!=(const Version& other) const - { - return !operator==(other); - } - - bool operator>=(const Version& other) const - { - if (MajorVersion > other.MajorVersion) return true; - if (MajorVersion < other.MajorVersion) return false; - return MinorVersion >= other.MinorVersion; - } - - // Gets a version that represents the latest schema known to the implementation. - static Version Latest(); - - // Gets a version that represents the latest schema known to the implementation for the given major version. - static Version LatestForMajor(uint32_t majorVersion); - - // Determines if this version represents the latest schema. - bool IsLatest() const; - - // Determines if this version represents the latest schema of the given major version. - bool IsLatestForMajor(uint32_t majorVersion) const; - - // Determines the schema version of the opened index. - static Version GetSchemaVersion(SQLite::Connection& connection); - - // Writes the current version to the given index. - void SetSchemaVersion(SQLite::Connection& connection); - }; - - // Output the version - std::ostream& operator<<(std::ostream& out, const Version& version); -} diff --git a/src/AppInstallerRepositoryCore/PackageTrackingCatalog.cpp b/src/AppInstallerRepositoryCore/PackageTrackingCatalog.cpp @@ -37,7 +37,7 @@ namespace AppInstaller::Repository if (!std::filesystem::exists(trackingDB)) { std::filesystem::create_directories(trackingDB.parent_path()); - return SQLiteIndex::CreateNew(trackingDB.u8string(), Schema::Version::Latest(), SQLiteIndex::CreateOptions::SupportPathless | SQLiteIndex::CreateOptions::DisableDependenciesSupport); + return SQLiteIndex::CreateNew(trackingDB.u8string(), SQLite::Version::Latest(), SQLiteIndex::CreateOptions::SupportPathless | SQLiteIndex::CreateOptions::DisableDependenciesSupport); } else { diff --git a/src/AppInstallerRepositoryCore/Rest/Schema/IRestClient.h b/src/AppInstallerRepositoryCore/Rest/Schema/IRestClient.h @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once -#include "Microsoft/Schema/Version.h" +#include <winget/SQLiteVersion.h> #include <winget/Manifest.h> #include <winget/RepositorySearch.h> #include <AppInstallerVersions.h> diff --git a/src/AppInstallerRepositoryCore/SQLiteStatementBuilder.cpp b/src/AppInstallerRepositoryCore/SQLiteStatementBuilder.cpp @@ -1,944 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#include "pch.h" -#include "SQLiteStatementBuilder.h" - -namespace AppInstaller::Repository::SQLite::Builder -{ - std::ostream& operator<<(std::ostream& out, const QualifiedColumn& column) - { - if (!column.Table.empty()) - { - out << '[' << column.Table << "]."; - } - out << '[' << column.Column << ']'; - return out; - } - - std::ostream& operator<<(std::ostream& out, const QualifiedTable& table) - { - if (!table.Schema.empty()) - { - out << '[' << table.Schema << "]."; - } - out << '[' << table.Table << ']'; - return out; - } - - std::ostream& operator<<(std::ostream& out, const details::SubBuilder& column) - { - out << column.GetString(); - return out; - } - - namespace - { - void OutputColumns(std::ostream& out, std::string_view op, std::string_view column) - { - out << op << '[' << column << ']'; - } - - void OutputColumns(std::ostream& out, std::string_view op, std::initializer_list<std::string_view> columns) - { - out << op; - bool isFirst = true; - for (const auto& c : columns) - { - out << (isFirst ? "[" : ", [") << c << ']'; - isFirst = false; - } - } - - void OutputColumns(std::ostream& out, std::string_view op, const QualifiedColumn& column) - { - out << op << column; - } - - void OutputColumns(std::ostream& out, std::string_view op, std::initializer_list<QualifiedColumn> columns) - { - out << op; - bool isFirst = true; - for (const auto& c : columns) - { - out << (isFirst ? "" : ", ") << c; - isFirst = false; - } - } - - void OutputColumns(std::ostream& out, std::string_view op, std::initializer_list<details::SubBuilder> columns) - { - out << op; - bool isFirst = true; - for (const auto& c : columns) - { - out << (isFirst ? "" : ", ") << c; - isFirst = false; - } - } - - void OutputAggregate(std::ostream& out, Aggregate op) - { - out << ' '; - switch (op) - { - case Aggregate::Min: - out << "MIN"; - break; - case Aggregate::Max: - out << "MAX"; - break; - default: - THROW_HR(E_UNEXPECTED); - } - } - - void OutputColumns(std::ostream& out, Aggregate op, std::string_view column) - { - OutputAggregate(out, op); - out << "([" << column << "])"; - } - - void OutputColumns(std::ostream& out, Aggregate op, const QualifiedColumn& column) - { - OutputAggregate(out, op); - out << '(' << column << ')'; - } - - // Use to output operation and table name, such as " FROM [table]" - void OutputOperationAndTable(std::ostream& out, std::string_view op, std::string_view table) - { - out << op << " [" << table << ']'; - } - - void OutputOperationAndTable(std::ostream& out, std::string_view op, QualifiedTable table) - { - out << op << table; - } - - void OutputOperationAndTable(std::ostream& out, std::string_view op, std::initializer_list<std::string_view> table) - { - out << op << " ["; - for (std::string_view t : table) - { - out << t; - } - out << ']'; - } - - void OutputType(std::ostream& out, Type type) - { - out << ' '; - switch (type) - { - case Type::Int: - out << "INT"; - break; - case Type::Int64: - out << "INT64"; - break; - case Type::Text: - out << "TEXT"; - break; - case Type::Blob: - out << "BLOB"; - break; - case Type::Integer: - out << "INTEGER"; - break; - default: - THROW_HR(E_UNEXPECTED); - } - } - } - - 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); - OutputType(m_stream, type); - } - - ColumnBuilder& ColumnBuilder::NotNull(bool isTrue) - { - if (isTrue) - { - m_stream << " NOT NULL"; - } - return *this; - } - - ColumnBuilder& ColumnBuilder::CollateNoCase(bool isTrue) - { - if (isTrue) - { - m_stream << " COLLATE NOCASE"; - } - return *this; - } - - ColumnBuilder& ColumnBuilder::Default(int64_t value) - { - m_stream << " DEFAULT " << value; - return *this; - } - - ColumnBuilder& ColumnBuilder::Unique(bool isTrue) - { - if (isTrue) - { - m_stream << " UNIQUE"; - } - return *this; - } - - ColumnBuilder& ColumnBuilder::PrimaryKey(bool isTrue) - { - if (isTrue) - { - m_stream << " PRIMARY KEY"; - } - return *this; - } - - PrimaryKeyBuilder::PrimaryKeyBuilder(std::initializer_list<std::string_view> columns) - { - OutputColumns(m_stream, "PRIMARY KEY(", columns); - m_stream << ')'; - m_needsClosing = false; - } - - PrimaryKeyBuilder::PrimaryKeyBuilder() - { - m_stream << "PRIMARY KEY("; - } - - PrimaryKeyBuilder& PrimaryKeyBuilder::Column(std::string_view column) - { - if (m_isFirst) - { - m_isFirst = false; - } - else - { - m_stream << ", "; - } - OutputColumns(m_stream, "", column); - return *this; - } - - PrimaryKeyBuilder::operator details::SubBuilder() - { - if (m_needsClosing) - { - m_stream << ')'; - m_needsClosing = false; - } - return { m_stream.str() }; - } - - StatementBuilder& StatementBuilder::Select() - { - m_stream << "SELECT "; - m_needsComma = false; - return *this; - } - - StatementBuilder& StatementBuilder::Select(std::string_view column) - { - OutputColumns(m_stream, "SELECT ", column); - return *this; - } - - StatementBuilder& StatementBuilder::Select(std::initializer_list<std::string_view> columns) - { - OutputColumns(m_stream, "SELECT ", columns); - return *this; - } - - StatementBuilder& StatementBuilder::Select(const QualifiedColumn& column) - { - OutputColumns(m_stream, "SELECT ", column); - return *this; - } - - StatementBuilder& StatementBuilder::Select(std::initializer_list<QualifiedColumn> columns) - { - OutputColumns(m_stream, "SELECT ", columns); - return *this; - } - - StatementBuilder& StatementBuilder::Select(details::rowcount_t) - { - m_stream << "SELECT COUNT(*)"; - return *this; - } - - StatementBuilder& StatementBuilder::From() - { - m_stream << " FROM "; - return *this; - } - - StatementBuilder& StatementBuilder::From(std::string_view table) - { - OutputOperationAndTable(m_stream, " FROM", table); - return *this; - } - - StatementBuilder& StatementBuilder::From(QualifiedTable table) - { - OutputOperationAndTable(m_stream, " FROM", table); - return *this; - } - - StatementBuilder& StatementBuilder::From(std::initializer_list<std::string_view> table) - { - OutputOperationAndTable(m_stream, " FROM", table); - return *this; - } - - StatementBuilder& StatementBuilder::Where(std::string_view column) - { - OutputColumns(m_stream, " WHERE ", column); - return *this; - } - - StatementBuilder& StatementBuilder::Where(const QualifiedColumn& column) - { - OutputColumns(m_stream, " WHERE ", column); - return *this; - } - - StatementBuilder& StatementBuilder::WhereValueContainsEmbeddedNullCharacter(std::string_view column) - { - OutputColumns(m_stream, " WHERE instr(", column); - m_stream << ",char(0))>0"; - return *this; - } - - StatementBuilder& StatementBuilder::WhereValueContainsEmbeddedNullCharacter(const QualifiedColumn& column) - { - OutputColumns(m_stream, " WHERE instr(", column); - m_stream << ",char(0))>0"; - return *this; - } - - StatementBuilder& StatementBuilder::Equals(details::unbound_t, std::optional<size_t> index) - { - AppendOpAndBinder(Op::Equals, index); - return *this; - } - - StatementBuilder& StatementBuilder::Equals(std::nullptr_t) - { - // This is almost certainly not what you want. - // In SQL, value = NULL is always false. - // Use StatementBuilder::IsNull instead. - THROW_HR(E_NOTIMPL); - } - - StatementBuilder& StatementBuilder::Equals() - { - m_stream << " ="; - return *this; - } - - StatementBuilder& StatementBuilder::LikeWithEscape(std::string_view value) - { - AddBindFunctor(AppendOpAndBinder(Op::Like), EscapeStringForLike(value)); - return Escape(EscapeCharForLike); - } - - StatementBuilder& StatementBuilder::Like(details::unbound_t) - { - AppendOpAndBinder(Op::Like); - return *this; - } - - StatementBuilder& StatementBuilder::LiteralColumn(std::string_view value) - { - if (m_needsComma) - { - m_stream << ", "; - } - AddBindFunctor(AppendOpAndBinder(Op::Literal), value); - m_needsComma = true; - return *this; - } - - StatementBuilder& StatementBuilder::Escape(std::string_view escapeChar) - { - THROW_HR_IF(E_INVALIDARG, escapeChar.length() != 1); - AddBindFunctor(AppendOpAndBinder(Op::Escape), escapeChar); - return *this; - } - - StatementBuilder& StatementBuilder::Not() - { - m_stream << " NOT"; - return *this; - } - - StatementBuilder& StatementBuilder::In() - { - m_stream << " IN"; - return *this; - } - - StatementBuilder& StatementBuilder::In(size_t count) - { - m_stream << " IN ("; - for (size_t i = 0; i < count; ++i) - { - m_stream << (i == 0 ? "?" : ", ?"); - } - m_stream << ')'; - - m_bindIndex += static_cast<int>(count); - return *this; - } - - StatementBuilder& StatementBuilder::IsNull(bool isNull) - { - m_stream << " IS " << (isNull ? "" : "NOT ") << "NULL"; - return *this; - } - - StatementBuilder& StatementBuilder::And(std::string_view column) - { - OutputColumns(m_stream, " AND ", column); - return *this; - } - - StatementBuilder& StatementBuilder::And(const QualifiedColumn& column) - { - OutputColumns(m_stream, " AND ", column); - return *this; - } - - StatementBuilder& StatementBuilder::Or(const QualifiedColumn& column) - { - OutputColumns(m_stream, " OR ", column); - return *this; - } - - StatementBuilder& StatementBuilder::Join(std::string_view table) - { - OutputOperationAndTable(m_stream, " JOIN", table); - return *this; - } - - StatementBuilder& StatementBuilder::Join(QualifiedTable table) - { - OutputOperationAndTable(m_stream, " JOIN", table); - return *this; - } - - StatementBuilder& StatementBuilder::Join(std::initializer_list<std::string_view> table) - { - OutputOperationAndTable(m_stream, " JOIN", table); - return *this; - } - - StatementBuilder& StatementBuilder::LeftOuterJoin(std::string_view table) - { - OutputOperationAndTable(m_stream, " LEFT OUTER JOIN", table); - return *this; - } - - StatementBuilder& StatementBuilder::LeftOuterJoin(QualifiedTable table) - { - OutputOperationAndTable(m_stream, " LEFT OUTER JOIN", table); - return *this; - } - - StatementBuilder& StatementBuilder::LeftOuterJoin(std::initializer_list<std::string_view> table) - { - OutputOperationAndTable(m_stream, " LEFT OUTER JOIN", table); - return *this; - } - - StatementBuilder& StatementBuilder::On(const QualifiedColumn& column1, const QualifiedColumn& column2) - { - m_stream << " ON " << column1 << " = " << column2; - return *this; - } - - StatementBuilder& StatementBuilder::Limit(size_t rowCount) - { - m_stream << " LIMIT " << rowCount; - return *this; - } - - StatementBuilder& StatementBuilder::GroupBy(std::string_view column) - { - OutputColumns(m_stream, " GROUP BY ", column); - return *this; - } - - StatementBuilder& StatementBuilder::GroupBy(const QualifiedColumn& column) - { - OutputColumns(m_stream, " GROUP BY ", column); - return *this; - } - - StatementBuilder& StatementBuilder::OrderBy(std::string_view column) - { - OutputColumns(m_stream, " ORDER BY ", column); - return *this; - } - - StatementBuilder& StatementBuilder::OrderBy(const QualifiedColumn& column) - { - OutputColumns(m_stream, " ORDER BY ", column); - return *this; - } - - StatementBuilder& StatementBuilder::Ascending() - { - m_stream << " ASC"; - return *this; - } - - StatementBuilder& StatementBuilder::Descending() - { - m_stream << " DESC"; - return *this; - } - - StatementBuilder& StatementBuilder::InsertInto(std::string_view table) - { - OutputOperationAndTable(m_stream, "INSERT INTO", table); - return *this; - } - - StatementBuilder& StatementBuilder::InsertInto(QualifiedTable table) - { - OutputOperationAndTable(m_stream, "INSERT INTO", table); - return *this; - } - - StatementBuilder& StatementBuilder::InsertInto(std::initializer_list<std::string_view> table) - { - OutputOperationAndTable(m_stream, "INSERT INTO", table); - return *this; - } - - StatementBuilder& StatementBuilder::Columns(std::string_view column) - { - OutputColumns(m_stream, "(", column); - m_stream << ')'; - return *this; - } - - StatementBuilder& StatementBuilder::Columns(std::initializer_list<std::string_view> columns) - { - OutputColumns(m_stream, "(", columns); - m_stream << ')'; - return *this; - } - - StatementBuilder& StatementBuilder::Columns(const QualifiedColumn& column) - { - OutputColumns(m_stream, "(", column); - m_stream << ')'; - return *this; - } - - StatementBuilder& StatementBuilder::Columns(std::initializer_list<QualifiedColumn> columns) - { - OutputColumns(m_stream, "(", columns); - m_stream << ')'; - return *this; - } - - StatementBuilder& StatementBuilder::Columns(std::initializer_list<details::SubBuilder> columns) - { - OutputColumns(m_stream, "(", columns); - m_stream << ')'; - return *this; - } - - StatementBuilder& StatementBuilder::BeginColumns() - { - m_stream << '('; - m_needsComma = false; - return *this; - } - - StatementBuilder& StatementBuilder::Column(std::string_view column) - { - if (m_needsComma) - { - m_stream << ", "; - } - OutputColumns(m_stream, "", column); - m_needsComma = true; - return *this; - } - - StatementBuilder& StatementBuilder::Column(const QualifiedColumn& column) - { - if (m_needsComma) - { - m_stream << ", "; - } - OutputColumns(m_stream, "", column); - m_needsComma = true; - return *this; - } - - StatementBuilder& StatementBuilder::Column(Aggregate aggOp, std::string_view column) - { - if (m_needsComma) - { - m_stream << ", "; - } - OutputColumns(m_stream, aggOp, column); - m_needsComma = true; - return *this; - } - - StatementBuilder& StatementBuilder::Column(Aggregate aggOp, const QualifiedColumn& column) - { - if (m_needsComma) - { - m_stream << ", "; - } - OutputColumns(m_stream, aggOp, column); - m_needsComma = true; - return *this; - } - - StatementBuilder& StatementBuilder::Column(const details::SubBuilder& column) - { - if (m_needsComma) - { - m_stream << ", "; - } - m_stream << column; - m_needsComma = true; - return *this; - } - - StatementBuilder& StatementBuilder::EndColumns() - { - m_stream << ')'; - m_needsComma = false; - return *this; - } - - StatementBuilder& StatementBuilder::BeginValues() - { - m_stream << " VALUES ("; - m_needsComma = false; - return *this; - } - - StatementBuilder& StatementBuilder::EndValues() - { - m_stream << ')'; - m_needsComma = false; - return *this; - } - - StatementBuilder& StatementBuilder::CreateTable(std::string_view table) - { - OutputOperationAndTable(m_stream, "CREATE TABLE", table); - return *this; - } - - StatementBuilder& StatementBuilder::CreateTable(QualifiedTable table) - { - OutputOperationAndTable(m_stream, "CREATE TABLE", table); - return *this; - } - - StatementBuilder& StatementBuilder::CreateTable(std::initializer_list<std::string_view> table) - { - OutputOperationAndTable(m_stream, "CREATE TABLE", table); - return *this; - } - - StatementBuilder& StatementBuilder::AlterTable(std::string_view table) - { - OutputOperationAndTable(m_stream, "ALTER TABLE", table); - return *this; - } - - StatementBuilder& StatementBuilder::AlterTable(QualifiedTable table) - { - OutputOperationAndTable(m_stream, "ALTER TABLE", table); - return *this; - } - - StatementBuilder& StatementBuilder::AlterTable(std::initializer_list<std::string_view> table) - { - OutputOperationAndTable(m_stream, "ALTER TABLE", table); - return *this; - } - - StatementBuilder& StatementBuilder::Add(std::string_view column, Type type) - { - m_stream << " ADD " << column; - OutputType(m_stream, type); - return *this; - } - - StatementBuilder& StatementBuilder::DropTable(std::string_view table) - { - OutputOperationAndTable(m_stream, "DROP TABLE", table); - return *this; - } - - StatementBuilder& StatementBuilder::DropTable(QualifiedTable table) - { - OutputOperationAndTable(m_stream, "DROP TABLE", table); - return *this; - } - - StatementBuilder& StatementBuilder::DropTable(std::initializer_list<std::string_view> table) - { - OutputOperationAndTable(m_stream, "DROP TABLE", table); - return *this; - } - - StatementBuilder& StatementBuilder::CreateIndex(std::string_view table) - { - OutputOperationAndTable(m_stream, "CREATE INDEX", table); - return *this; - } - - StatementBuilder& StatementBuilder::CreateIndex(QualifiedTable table) - { - OutputOperationAndTable(m_stream, "CREATE INDEX", table); - return *this; - } - - StatementBuilder& StatementBuilder::CreateIndex(std::initializer_list<std::string_view> table) - { - OutputOperationAndTable(m_stream, "CREATE INDEX", table); - return *this; - } - - StatementBuilder& StatementBuilder::CreateUniqueIndex(std::string_view table) - { - OutputOperationAndTable(m_stream, "CREATE UNIQUE INDEX", table); - return *this; - } - - StatementBuilder& StatementBuilder::CreateUniqueIndex(QualifiedTable table) - { - OutputOperationAndTable(m_stream, "CREATE UNIQUE INDEX", table); - return *this; - } - - StatementBuilder& StatementBuilder::CreateUniqueIndex(std::initializer_list<std::string_view> table) - { - OutputOperationAndTable(m_stream, "CREATE UNIQUE INDEX", table); - return *this; - } - - StatementBuilder& StatementBuilder::DropIndex(std::string_view index) - { - OutputOperationAndTable(m_stream, "DROP INDEX", index); - return *this; - } - - StatementBuilder& StatementBuilder::DropIndex(QualifiedTable index) - { - OutputOperationAndTable(m_stream, "DROP INDEX", index); - return *this; - } - - StatementBuilder& StatementBuilder::DropIndex(std::initializer_list<std::string_view> index) - { - OutputOperationAndTable(m_stream, "DROP INDEX", index); - return *this; - } - - StatementBuilder& StatementBuilder::On(std::string_view table) - { - OutputOperationAndTable(m_stream, " ON", table); - return *this; - } - - StatementBuilder& StatementBuilder::On(std::initializer_list<std::string_view> table) - { - OutputOperationAndTable(m_stream, " ON", table); - return *this; - } - - StatementBuilder& StatementBuilder::DeleteFrom(std::string_view table) - { - OutputOperationAndTable(m_stream, "DELETE FROM", table); - return *this; - } - - StatementBuilder& StatementBuilder::DeleteFrom(QualifiedTable table) - { - OutputOperationAndTable(m_stream, "DELETE FROM", table); - return *this; - } - - StatementBuilder& StatementBuilder::DeleteFrom(std::initializer_list<std::string_view> table) - { - OutputOperationAndTable(m_stream, "DELETE FROM", table); - return *this; - } - - StatementBuilder& StatementBuilder::Update(std::string_view table) - { - OutputOperationAndTable(m_stream, "UPDATE", table); - return *this; - } - - StatementBuilder& StatementBuilder::Update(QualifiedTable table) - { - OutputOperationAndTable(m_stream, "UPDATE", table); - return *this; - } - - StatementBuilder& StatementBuilder::Update(std::initializer_list<std::string_view> table) - { - OutputOperationAndTable(m_stream, "UPDATE", table); - return *this; - } - - StatementBuilder& StatementBuilder::UpdateOrReplace(std::string_view table) - { - OutputOperationAndTable(m_stream, "UPDATE OR REPLACE", table); - return *this; - } - - StatementBuilder& StatementBuilder::UpdateOrReplace(QualifiedTable table) - { - OutputOperationAndTable(m_stream, "UPDATE OR REPLACE", table); - return *this; - } - - StatementBuilder& StatementBuilder::UpdateOrReplace(std::initializer_list<std::string_view> table) - { - OutputOperationAndTable(m_stream, "UPDATE OR REPLACE", table); - return *this; - } - - StatementBuilder& StatementBuilder::Set() - { - m_stream << " SET "; - m_needsComma = false; - return *this; - } - - StatementBuilder& StatementBuilder::Vacuum() - { - m_stream << "VACUUM"; - return *this; - } - - StatementBuilder& StatementBuilder::BeginParenthetical() - { - m_stream << '('; - return *this; - } - - StatementBuilder& StatementBuilder::EndParenthetical() - { - m_stream << ')'; - return *this; - } - - StatementBuilder& StatementBuilder::WithoutRowID() - { - m_stream << " WITHOUT ROWID"; - return *this; - } - - - StatementBuilder& StatementBuilder::As(std::string_view alias) - { - OutputOperationAndTable(m_stream, " AS", alias); - return *this; - } - - Statement StatementBuilder::Prepare(const Connection& connection) - { - Statement result = Statement::Create(connection, m_stream.str()); - for (const auto& f : m_binders) - { - f(result); - } - return result; - } - - void StatementBuilder::Execute(const Connection& connection) - { - Prepare(connection).Execute(); - } - - int StatementBuilder::AppendOpAndBinder(Op op, std::optional<size_t> index) - { - switch (op) - { - case Op::Equals: - m_stream << " = ?"; - break; - case Op::Like: - m_stream << " LIKE ?"; - break; - case Op::Escape: - m_stream << " ESCAPE ?"; - break; - case Op::Literal: - m_stream << " ?"; - break; - default: - THROW_HR(E_UNEXPECTED); - } - - if (index) - { - m_stream << index.value(); - } - - return m_bindIndex++; - } - - int StatementBuilder::AppendValuesAndBinders(size_t count) - { - m_stream << " VALUES ("; - for (size_t i = 0; i < count; ++i) - { - m_stream << (i == 0 ? "?" : ", ?"); - } - m_stream << ')'; - - int result = m_bindIndex; - m_bindIndex += static_cast<int>(count); - return result; - } - - int StatementBuilder::AppendValueAndBinder() - { - if (m_needsComma) - { - m_stream << ", "; - } - m_stream << '?'; - m_needsComma = true; - return m_bindIndex++; - } -} diff --git a/src/AppInstallerRepositoryCore/SQLiteStatementBuilder.h b/src/AppInstallerRepositoryCore/SQLiteStatementBuilder.h @@ -1,499 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#pragma once -#include "SQLiteWrapper.h" -#include <AppInstallerLanguageUtilities.h> - -#include <functional> -#include <initializer_list> -#include <memory> -#include <optional> -#include <sstream> -#include <string_view> -#include <vector> - -using namespace std::string_view_literals; - -namespace AppInstaller::Repository::SQLite::Builder -{ - namespace details - { - // Sentinel types to indicate special cases to the builder. - struct unbound_t {}; - struct rowcount_t {}; - - // Class for intake from external functions. - struct SubBuilder - { - SubBuilder(std::string&& s) : m_string(std::move(s)) {} - - SubBuilder(const SubBuilder&) = default; - SubBuilder& operator=(const SubBuilder&) = default; - - SubBuilder(SubBuilder&&) noexcept = default; - SubBuilder& operator=(SubBuilder&&) noexcept = default; - - const std::string& GetString() const { return m_string; } - - protected: - std::string m_string; - }; - - // Base class for all sub-builders. - struct SubBuilderBase - { - SubBuilderBase() = default; - - SubBuilderBase(const SubBuilderBase&) = default; - SubBuilderBase& operator=(const SubBuilderBase&) = default; - - SubBuilderBase(SubBuilderBase&&) noexcept = default; - SubBuilderBase& operator=(SubBuilderBase&&) noexcept = default; - - virtual operator SubBuilder() { return { m_stream.str() }; } - - protected: - std::ostringstream m_stream; - }; - } - - // Pass this value to indicate that the caller will bind the value later. - __declspec_selectany_ details::unbound_t Unbound; - - // Pass this value to indicate that the number of rows is to be selected. - __declspec_selectany_ details::rowcount_t RowCount; - - // A qualified table reference. - struct QualifiedTable - { - std::string_view Schema; - std::string_view Table; - - explicit constexpr QualifiedTable(std::string_view table) : Table(table) {} - explicit constexpr QualifiedTable(std::string_view schema, std::string_view table) : Schema(schema), Table(table) {} - }; - - namespace Schema - { - // The main database's schema table. - // More info can be found at: https://www.sqlite.org/schematab.html - constexpr QualifiedTable MainTable{ "main"sv, "sqlite_master"sv }; - - // The sqlite_schema column name for the type of the object. - constexpr std::string_view TypeColumn = "type"sv; - - // The sqlite_schema type value for a table. - constexpr std::string_view Type_Table = "table"sv; - - // The sqlite_schema type value for an index. - constexpr std::string_view Type_Index = "index"sv; - - // The sqlite_schema column name for the name of the object. - constexpr std::string_view NameColumn = "name"sv; - } - - // A qualified column reference. - struct QualifiedColumn - { - std::string_view Table; - std::string_view Column; - - explicit QualifiedColumn(std::string_view column) : Column(column) {} - explicit QualifiedColumn(std::string_view table, std::string_view column) : Table(table), Column(column) {} - }; - - // SQLite types as an enum. - enum class Type - { - Int, - Bool = Int, - Int64, - RowId = Int64, - Text, - Blob, - Integer, // Type for specifying a primary key column as a row id alias. - }; - - // Aggregate functions. - enum class Aggregate - { - Min, - Max, - }; - - // 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 - { - // Specify the column name and type when creating the builder. - ColumnBuilder(std::string_view column, Type type); - - ColumnBuilder(const ColumnBuilder&) = default; - ColumnBuilder& operator=(const ColumnBuilder&) = default; - - ColumnBuilder(ColumnBuilder&&) noexcept = default; - ColumnBuilder& operator=(ColumnBuilder&&) noexcept = default; - - // Indicate that the column is not able to be null. - // Allow for data driven construction with input value. - ColumnBuilder& NotNull(bool isTrue = true); - - // Indicate that the column is case-insensitive. - // Allow for data driven construction with input value. - ColumnBuilder& CollateNoCase(bool isTrue = true); - - // Indicate the default value for the column. - // Note that a default value is not considered constant if it is bound, - // so this function directly places the incoming value into the SQL statement. - ColumnBuilder& Default(int64_t value); - - // Indicate that the column is unique. - // Allow for data driven construction with input value. - ColumnBuilder& Unique(bool isTrue = true); - - // Indicate that the column is the primary key. - // Allow for data driven construction with input value. - ColumnBuilder& PrimaryKey(bool isTrue = true); - }; - - // Helper used to specify a primary key with multiple columns. - struct PrimaryKeyBuilder : public details::SubBuilderBase - { - PrimaryKeyBuilder(); - PrimaryKeyBuilder(std::initializer_list<std::string_view> columns); - - PrimaryKeyBuilder(const PrimaryKeyBuilder&) = default; - PrimaryKeyBuilder& operator=(const PrimaryKeyBuilder&) = default; - - PrimaryKeyBuilder(PrimaryKeyBuilder&&) noexcept = default; - PrimaryKeyBuilder& operator=(PrimaryKeyBuilder&&) noexcept = default; - - virtual operator details::SubBuilder() override; - - // Add a column to the primary key. - PrimaryKeyBuilder& Column(std::string_view column); - - private: - bool m_isFirst = true; - bool m_needsClosing = true; - }; - - // A class that aids in building SQL statements in a more expressive manner than simple strings. - struct StatementBuilder - { - StatementBuilder() = default; - - StatementBuilder(const StatementBuilder&) = default; - StatementBuilder& operator=(const StatementBuilder&) = default; - - StatementBuilder(StatementBuilder&&) = default; - StatementBuilder& operator=(StatementBuilder&&) = default; - - // Begin a select statement for the given columns. - StatementBuilder& Select(); - StatementBuilder& Select(std::string_view column); - StatementBuilder& Select(std::initializer_list<std::string_view> columns); - StatementBuilder& Select(const QualifiedColumn& column); - StatementBuilder& Select(std::initializer_list<QualifiedColumn> columns); - StatementBuilder& Select(details::rowcount_t); - - // Indicate the table that the statement will be operating on. - // The initializer_list form enables the table name to be constructed from multiple parts. - StatementBuilder& From(); - StatementBuilder& From(std::string_view table); - StatementBuilder& From(QualifiedTable table); - StatementBuilder& From(std::initializer_list<std::string_view> table); - - // Begin a filter clause on the given column. - StatementBuilder& Where(std::string_view column); - StatementBuilder& Where(const QualifiedColumn& column); - - // A full filter clause looking for an embedded null character. - // Is extremely specific to consistency checks, and so a more detailed construct is not required. - StatementBuilder& WhereValueContainsEmbeddedNullCharacter(std::string_view column); - StatementBuilder& WhereValueContainsEmbeddedNullCharacter(const QualifiedColumn& column); - - // Indicate the operation of the filter clause. - template <typename ValueType> - StatementBuilder& Equals(const ValueType& value) - { - AddBindFunctor(AppendOpAndBinder(Op::Equals), value); - return *this; - } - template <typename ValueType> - StatementBuilder& Equals(const std::optional<ValueType>& value) - { - if (value) - { - AddBindFunctor(AppendOpAndBinder(Op::Equals), value.value()); - return *this; - } - else - { - return IsNull(); - } - } - // The optional index value can be used to specify the parameter index. - StatementBuilder& Equals(details::unbound_t, std::optional<size_t> index = {}); - StatementBuilder& Equals(std::nullptr_t); - StatementBuilder& Equals(); - - StatementBuilder& LikeWithEscape(std::string_view value); - StatementBuilder& Like(details::unbound_t); - - StatementBuilder& LiteralColumn(std::string_view value); - - StatementBuilder& Escape(std::string_view escapeChar); - - StatementBuilder& Not(); - StatementBuilder& In(); - - // Appends a set of value binders for the In clause. - StatementBuilder& In(size_t count); - - // IsNull(true) means the value is null; IsNull(false) means the value is not null. - StatementBuilder& IsNull(bool isNull = true); - StatementBuilder& IsNotNull() { return IsNull(false); } - - // Operators for combining filter clauses. - StatementBuilder& And(std::string_view column); - StatementBuilder& And(const QualifiedColumn& column); - StatementBuilder& Or(const QualifiedColumn& column); - - // Begin a join clause. - // The initializer_list form enables the table name to be constructed from multiple parts. - StatementBuilder& Join(std::string_view table); - StatementBuilder& Join(QualifiedTable table); - StatementBuilder& Join(std::initializer_list<std::string_view> table); - - // Begin a left outer join clause. - // The initializer_list form enables the table name to be constructed from multiple parts. - StatementBuilder& LeftOuterJoin(std::string_view table); - StatementBuilder& LeftOuterJoin(QualifiedTable table); - StatementBuilder& LeftOuterJoin(std::initializer_list<std::string_view> table); - - // Set the join constraint. - StatementBuilder& On(const QualifiedColumn& column1, const QualifiedColumn& column2); - - // Specify the grouping to use. - StatementBuilder& GroupBy(std::string_view column); - StatementBuilder& GroupBy(const QualifiedColumn& column); - - // Specify the ordering to use. - StatementBuilder& OrderBy(std::string_view column); - StatementBuilder& OrderBy(const QualifiedColumn& column); - - // Specify the ordering behavior. - StatementBuilder& Ascending(); - StatementBuilder& Descending(); - - // Limits the result set to the given number of rows. - StatementBuilder& Limit(size_t rowCount); - - // Begin an insert statement for the given table. - // The initializer_list form enables the table name to be constructed from multiple parts. - StatementBuilder& InsertInto(std::string_view table); - StatementBuilder& InsertInto(QualifiedTable table); - StatementBuilder& InsertInto(std::initializer_list<std::string_view> table); - - // Set the columns for a statement (typically insert). - StatementBuilder& Columns(std::string_view column); - StatementBuilder& Columns(std::initializer_list<std::string_view> columns); - StatementBuilder& Columns(const QualifiedColumn& column); - StatementBuilder& Columns(std::initializer_list<QualifiedColumn> columns); - - // Set the columns for a select or create table statement. - StatementBuilder& Columns(std::initializer_list<details::SubBuilder> columns); - StatementBuilder& BeginColumns(); - StatementBuilder& Column(std::string_view column); - StatementBuilder& Column(const QualifiedColumn& column); - StatementBuilder& Column(Aggregate aggOp, std::string_view column); - StatementBuilder& Column(Aggregate aggOp, const QualifiedColumn& column); - StatementBuilder& Column(const details::SubBuilder& column); - StatementBuilder& EndColumns(); - - // Add the values clause for an insert statement. - template <typename... ValueTypes> - StatementBuilder& Values(const ValueTypes&... values) - { - int bindIndexBegin = AppendValuesAndBinders(sizeof...(ValueTypes)); - // Use folding to add a binder for every value, specifically in the order they were given. - // Do not change this expression without understanding the implications to the bind order. - // See: https://en.cppreference.com/w/cpp/language/fold for more details. - (FoldHelper{}, ..., InsertValuesValueBinder(bindIndexBegin++, values)); - return *this; - } - StatementBuilder& BeginValues(); - template <typename ValueType> - StatementBuilder& Value(const ValueType& value) - { - InsertValuesValueBinder(AppendValueAndBinder(), value); - return *this; - } - StatementBuilder& EndValues(); - - // Begin a table creation statement. - // The initializer_list form enables the table name to be constructed from multiple parts. - StatementBuilder& CreateTable(std::string_view table); - StatementBuilder& CreateTable(QualifiedTable table); - StatementBuilder& CreateTable(std::initializer_list<std::string_view> table); - - // Begin an alter table statement. - // The initializer_list form enables the table name to be constructed from multiple parts. - StatementBuilder& AlterTable(std::string_view table); - StatementBuilder& AlterTable(QualifiedTable table); - StatementBuilder& AlterTable(std::initializer_list<std::string_view> table); - - // Complete an alter table statement by adding a column. - StatementBuilder& Add(std::string_view column, Type type); - - // Begin an table deletion statement. - // The initializer_list form enables the table name to be constructed from multiple parts. - StatementBuilder& DropTable(std::string_view table); - StatementBuilder& DropTable(QualifiedTable table); - StatementBuilder& DropTable(std::initializer_list<std::string_view> table); - - // Begin an index creation statement. - // The initializer_list form enables the index name to be constructed from multiple parts. - StatementBuilder& CreateIndex(std::string_view table); - StatementBuilder& CreateIndex(QualifiedTable table); - StatementBuilder& CreateIndex(std::initializer_list<std::string_view> table); - - // Begin an unique index creation statement. - // The initializer_list form enables the index name to be constructed from multiple parts. - StatementBuilder& CreateUniqueIndex(std::string_view table); - StatementBuilder& CreateUniqueIndex(QualifiedTable table); - StatementBuilder& CreateUniqueIndex(std::initializer_list<std::string_view> table); - - // Begin an index deletion statement. - // The initializer_list form enables the table name to be constructed from multiple parts. - StatementBuilder& DropIndex(std::string_view index); - StatementBuilder& DropIndex(QualifiedTable index); - StatementBuilder& DropIndex(std::initializer_list<std::string_view> index); - - // Set index target table. - StatementBuilder& On(std::string_view table); - StatementBuilder& On(std::initializer_list<std::string_view> table); - - // Begin a delete statement. - // The initializer_list form enables the table name to be constructed from multiple parts. - StatementBuilder& DeleteFrom(std::string_view table); - StatementBuilder& DeleteFrom(QualifiedTable table); - StatementBuilder& DeleteFrom(std::initializer_list<std::string_view> table); - - // Begin an update statement. - // The initializer_list form enables the table name to be constructed from multiple parts. - StatementBuilder& Update(std::string_view table); - StatementBuilder& Update(QualifiedTable table); - StatementBuilder& Update(std::initializer_list<std::string_view> table); - - // Begin an `update or replace` statement. - // The initializer_list form enables the table name to be constructed from multiple parts. - StatementBuilder& UpdateOrReplace(std::string_view table); - StatementBuilder& UpdateOrReplace(QualifiedTable table); - StatementBuilder& UpdateOrReplace(std::initializer_list<std::string_view> table); - - // Output the set portion of an update statement. - StatementBuilder& Set(); - - // Output the set portion of an update statement. - StatementBuilder& Vacuum(); - - // General purpose functions to begin and end a parenthetical expression. - StatementBuilder& BeginParenthetical(); - StatementBuilder& EndParenthetical(); - - // Adds the `without rowid` clause. - StatementBuilder& WithoutRowID(); - - // Assign an alias to the previous item. - StatementBuilder& As(std::string_view alias); - - // Gets the last bound index. - // A value of zero indicates that nothing has been bound. - int GetLastBindIndex() const { return m_bindIndex - 1; } - - // Prepares and returns the statement, applying any bindings that were requested. - Statement Prepare(const Connection& connection); - - // A convenience function that prepares, binds, and then executes a statement that does not return rows. - void Execute(const Connection& connection); - - private: - enum class Op - { - Equals, - Like, - Escape, - Literal, - }; - - // Appends given the operation. - // The optional index value can be used to specify the parameter index. - int AppendOpAndBinder(Op op, std::optional<size_t> index = {}); - - // Appends a set of binders for the values clause of an insert. - int AppendValuesAndBinders(size_t count); - - // Appends a binder for the values clause of an insert. - int AppendValueAndBinder(); - - // Adds a functor to our list that will bind the given value. - template <typename ValueType> - void AddBindFunctor(int binderIndex, const ValueType& value) - { - m_binders.emplace_back([binderIndex, value](Statement& s) { s.Bind(binderIndex, value); }); - } - - // Helper template for binding incoming values for an insert. - template <typename ValueType> - StatementBuilder& InsertValuesValueBinder(int bindIndex, const ValueType& value) - { - AddBindFunctor(bindIndex, value); - return *this; - } - template <typename ValueType> - StatementBuilder& InsertValuesValueBinder(int bindIndex, const std::optional<ValueType>& value) - { - if (value) - { - AddBindFunctor(bindIndex, value.value()); - } - else - { - AddBindFunctor(bindIndex, nullptr); - } - return *this; - } - StatementBuilder& InsertValuesValueBinder(int, details::unbound_t) - { - return *this; - } - StatementBuilder& InsertValuesValueBinder(int bindIndex, std::nullptr_t) - { - AddBindFunctor(bindIndex, nullptr); - return *this; - } - - std::ostringstream m_stream; - // Because binding values starts at 1 - int m_bindIndex = 1; - std::vector<std::function<void(Statement&)>> m_binders; - bool m_needsComma = false; - }; -} diff --git a/src/AppInstallerRepositoryCore/SQLiteTempTable.cpp b/src/AppInstallerRepositoryCore/SQLiteTempTable.cpp @@ -1,42 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#include "pch.h" -#include "SQLiteTempTable.h" - - -namespace AppInstaller::Repository::SQLite -{ - using namespace std::string_view_literals; - - TempTable::TempTable() - { - GUID tempName; - THROW_IF_FAILED(CoCreateGuid(&tempName)); - - wchar_t guidAsString[MAX_PATH]; - THROW_HR_IF(E_UNEXPECTED, StringFromGUID2(tempName, guidAsString, MAX_PATH) == 0); - - m_name = Utility::ConvertToUTF8(guidAsString); - } - - TempTable::~TempTable() - { - if (m_dropTableStatement) - { - m_dropTableStatement.Execute(); - } - } - - Builder::QualifiedTable TempTable::GetQualifiedName() const - { - return Builder::QualifiedTable("temp"sv, m_name); - } - - void TempTable::InitDropStatement(const Connection& connection) - { - Builder::StatementBuilder builder; - builder.DropTable(m_name); - - m_dropTableStatement = builder.Prepare(connection); - } -} diff --git a/src/AppInstallerRepositoryCore/SQLiteTempTable.h b/src/AppInstallerRepositoryCore/SQLiteTempTable.h @@ -1,35 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#pragma once -#include "SQLiteWrapper.h" -#include "SQLiteStatementBuilder.h" - - -namespace AppInstaller::Repository::SQLite -{ - // The base for a class that represents a temp table. - struct TempTable - { - TempTable(); - - ~TempTable(); - - TempTable(const TempTable&) = delete; - TempTable& operator=(const TempTable&) = delete; - - TempTable(TempTable&&) = default; - TempTable& operator=(TempTable&&) = default; - - protected: - // Gets the qualified name of the temp table. - Builder::QualifiedTable GetQualifiedName() const; - - // Prepares the drop table statement for use in destructor. - // It needs to be run by the derived class after the table is actually created. - void InitDropStatement(const Connection& connection); - - private: - std::string m_name; - Statement m_dropTableStatement; - }; -} diff --git a/src/AppInstallerRepositoryCore/SQLiteWrapper.cpp b/src/AppInstallerRepositoryCore/SQLiteWrapper.cpp @@ -1,374 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#include "pch.h" -#include "SQLiteWrapper.h" -#include "ICU/SQLiteICU.h" - -#include <wil/result_macros.h> - -using namespace std::string_view_literals; - -// Enable this to have all Statement constructions output the associated query plan. -#define WINGET_SQLITE_EXPLAIN_QUERY_PLAN_ENABLED 0 - -#if WINGET_SQLITE_EXPLAIN_QUERY_PLAN_ENABLED -#include <stack> -#endif - -#define THROW_SQLITE(_error_,_connection_) \ - do { \ - int _ts_sqliteReturnValue = _error_; \ - sqlite3* _ts_sqliteConnection = _connection_; \ - THROW_EXCEPTION_MSG(SQLiteException(_ts_sqliteReturnValue), _ts_sqliteConnection ? sqlite3_errmsg(_ts_sqliteConnection) : sqlite3_errstr(_ts_sqliteReturnValue)); \ - } while (0,0) - -#define THROW_IF_SQLITE_FAILED(_statement_,_connection_) \ - do { \ - int _tisf_sqliteReturnValue = _statement_; \ - if (_tisf_sqliteReturnValue != SQLITE_OK) \ - { \ - THROW_SQLITE(_tisf_sqliteReturnValue,_connection_); \ - } \ - } while (0,0) - -namespace AppInstaller::Repository::SQLite -{ - std::string_view RowIDName = "rowid"sv; - - namespace - { - size_t GetNextConnectionId() - { - static std::atomic_size_t connectionId(0); - return ++connectionId; - } - - size_t GetNextStatementId() - { - static std::atomic_size_t statementId(0); - return ++statementId; - } - } - - namespace details - { - void ParameterSpecificsImpl<nullptr_t>::Bind(sqlite3_stmt* stmt, int index, nullptr_t) - { - THROW_IF_SQLITE_FAILED(sqlite3_bind_null(stmt, index), sqlite3_db_handle(stmt)); - } - - void ThrowIfContainsEmbeddedNullCharacter(std::string_view v) - { - THROW_HR_IF(APPINSTALLER_CLI_ERROR_BIND_WITH_EMBEDDED_NULL, v.find('\0') != std::string_view::npos); - } - - void ParameterSpecificsImpl<std::string>::Bind(sqlite3_stmt* stmt, int index, const std::string& v) - { - ThrowIfContainsEmbeddedNullCharacter(v); - THROW_IF_SQLITE_FAILED(sqlite3_bind_text64(stmt, index, v.c_str(), v.size(), SQLITE_TRANSIENT, SQLITE_UTF8), sqlite3_db_handle(stmt)); - } - - std::string ParameterSpecificsImpl<std::string>::GetColumn(sqlite3_stmt* stmt, int column) - { - return reinterpret_cast<const char*>(sqlite3_column_text(stmt, column)); - } - - void ParameterSpecificsImpl<std::string_view>::Bind(sqlite3_stmt* stmt, int index, std::string_view v) - { - if (v.empty()) - { - // An empty string_view can have it's data member return nullptr, which effectively binds a null value. - // We don't want that, so instead bind an empty string, which will have a non-null data pointer. - ParameterSpecificsImpl<std::string>::Bind(stmt, index, {}); - } - else - { - ThrowIfContainsEmbeddedNullCharacter(v); - THROW_IF_SQLITE_FAILED(sqlite3_bind_text64(stmt, index, v.data(), v.size(), SQLITE_TRANSIENT, SQLITE_UTF8), sqlite3_db_handle(stmt)); - } - } - - void ParameterSpecificsImpl<int>::Bind(sqlite3_stmt* stmt, int index, int v) - { - THROW_IF_SQLITE_FAILED(sqlite3_bind_int(stmt, index, v), sqlite3_db_handle(stmt)); - } - - int ParameterSpecificsImpl<int>::GetColumn(sqlite3_stmt* stmt, int column) - { - return sqlite3_column_int(stmt, column); - } - - void ParameterSpecificsImpl<int64_t>::Bind(sqlite3_stmt* stmt, int index, int64_t v) - { - THROW_IF_SQLITE_FAILED(sqlite3_bind_int64(stmt, index, v), sqlite3_db_handle(stmt)); - } - - int64_t ParameterSpecificsImpl<int64_t>::GetColumn(sqlite3_stmt* stmt, int column) - { - return sqlite3_column_int64(stmt, column); - } - - void ParameterSpecificsImpl<bool>::Bind(sqlite3_stmt* stmt, int index, bool v) - { - THROW_IF_SQLITE_FAILED(sqlite3_bind_int(stmt, index, (v ? 1 : 0)), sqlite3_db_handle(stmt)); - } - - bool ParameterSpecificsImpl<bool>::GetColumn(sqlite3_stmt* stmt, int column) - { - return (sqlite3_column_int(stmt, column) != 0); - } - - std::string ParameterSpecificsImpl<blob_t>::ToLog(const blob_t& v) - { - std::ostringstream strstr; - strstr << "blob[" << v.size() << "]"; - return strstr.str(); - } - - void ParameterSpecificsImpl<blob_t>::Bind(sqlite3_stmt* stmt, int index, const blob_t& v) - { - THROW_IF_SQLITE_FAILED(sqlite3_bind_blob64(stmt, index, v.data(), v.size(), SQLITE_TRANSIENT), sqlite3_db_handle(stmt)); - } - - blob_t ParameterSpecificsImpl<blob_t>::GetColumn(sqlite3_stmt* stmt, int column) - { - const blob_t::value_type* blobPtr = reinterpret_cast<const blob_t::value_type *>(sqlite3_column_blob(stmt, column)); - if (blobPtr) - { - int blobBytes = sqlite3_column_bytes(stmt, column); - return blob_t{ blobPtr, blobPtr + blobBytes }; - } - else - { - return {}; - } - } - } - - Connection::Connection(const std::string& target, OpenDisposition disposition, OpenFlags flags) - { - m_id = GetNextConnectionId(); - AICLI_LOG(SQL, Info, << "Opening SQLite connection #" << m_id << ": '" << target << "' [" << std::hex << static_cast<int>(disposition) << ", " << std::hex << static_cast<int>(flags) << "]"); - // Always force connection serialization until we determine that there are situations where it is not needed - int resultingFlags = static_cast<int>(disposition) | static_cast<int>(flags) | SQLITE_OPEN_FULLMUTEX; - THROW_IF_SQLITE_FAILED(sqlite3_open_v2(target.c_str(), &m_dbconn, resultingFlags, nullptr), nullptr); - } - - Connection Connection::Create(const std::string& target, OpenDisposition disposition, OpenFlags flags) - { - Connection result{ target, disposition, flags }; - - THROW_IF_SQLITE_FAILED(sqlite3_extended_result_codes(result.m_dbconn.get(), 1), result.m_dbconn.get()); - - return result; - } - - void Connection::EnableICU() - { - AICLI_LOG(SQL, Verbose, << "Enabling ICU"); - THROW_IF_SQLITE_FAILED(sqlite3IcuInit(m_dbconn.get()), m_dbconn.get()); - } - - rowid_t Connection::GetLastInsertRowID() - { - return sqlite3_last_insert_rowid(m_dbconn.get()); - } - - int Connection::GetChanges() const - { - return sqlite3_changes(m_dbconn.get()); - } - - size_t Connection::GetID() const - { - return m_id; - } - - Statement::Statement(const Connection& connection, std::string_view sql) - { - m_connectionId = connection.GetID(); - m_id = GetNextStatementId(); - AICLI_LOG(SQL, Verbose, << "Preparing statement #" << m_connectionId << '-' << m_id << ": " << sql); - // SQL string size should include the null terminator (https://www.sqlite.org/c3ref/prepare.html) - assert(sql.data()[sql.size()] == '\0'); - THROW_IF_SQLITE_FAILED(sqlite3_prepare_v2(connection, sql.data(), static_cast<int>(sql.size() + 1), &m_stmt, nullptr), connection); - } - -#if WINGET_SQLITE_EXPLAIN_QUERY_PLAN_ENABLED -#define WINGET_SQLITE_EXPLAIN_QUERY_PLAN(_connection_,_sql_) \ - std::string _explainStatementSQL_ = "EXPLAIN QUERY PLAN "; \ - _explainStatementSQL_.append(_sql_); \ - try { \ - Statement _explainStatement_(_connection_,_explainStatementSQL_); \ - LogExplainQueryPlanResult(_sql_, _explainStatement_); \ - } catch(...) {} - - void LogExplainQueryPlanResult(std::string_view sql, Statement& plan) - { - bool outputHeader = true; - std::stack<int> parents; - - while (plan.Step()) - { - if (outputHeader) - { - AICLI_LOG(SQL, Info, << "Query plan for: " << sql); - outputHeader = false; - } - - int id = plan.GetColumn<int>(0); - int parent = plan.GetColumn<int>(1); - - while (!parents.empty() && parents.top() != parent) - { - parents.pop(); - } - - AICLI_LOG(SQL, Info, << "|-" << std::string(parents.size() * 2, '-') << ' ' << plan.GetColumn<std::string>(3)); - - parents.push(id); - } - } -#else -#define WINGET_SQLITE_EXPLAIN_QUERY_PLAN(_connection_,_sql_) -#endif - - Statement Statement::Create(const Connection& connection, const std::string& sql) - { - WINGET_SQLITE_EXPLAIN_QUERY_PLAN(connection, sql); - return { connection, { sql.c_str(), sql.size() } }; - } - - Statement Statement::Create(const Connection& connection, std::string_view sql) - { - WINGET_SQLITE_EXPLAIN_QUERY_PLAN(connection, sql); - // We need the statement to be null terminated, and the only way to guarantee that with a string_view is to construct a string copy. - return Create(connection, std::string(sql)); - } - - Statement Statement::Create(const Connection& connection, char const* const sql) - { - WINGET_SQLITE_EXPLAIN_QUERY_PLAN(connection, sql); - return { connection, sql }; - } - - bool Statement::Step(bool failFastOnError) - { - AICLI_LOG(SQL, Verbose, << "Stepping statement #" << m_connectionId << '-' << m_id); - int result = sqlite3_step(m_stmt.get()); - - if (result == SQLITE_ROW) - { - AICLI_LOG(SQL, Verbose, << "Statement #" << m_connectionId << '-' << m_id << " has data"); - m_state = State::HasRow; - return true; - } - else if (result == SQLITE_DONE) - { - AICLI_LOG(SQL, Verbose, << "Statement #" << m_connectionId << '-' << m_id << " has completed"); - m_state = State::Completed; - return false; - } - else - { - m_state = State::Error; - if (failFastOnError) - { - FAIL_FAST_MSG("Critical SQL statement failed"); - } - else - { - THROW_SQLITE(result, sqlite3_db_handle(m_stmt.get())); - } - } - } - - void Statement::Execute(bool failFastOnError) - { - THROW_HR_IF(E_UNEXPECTED, Step(failFastOnError)); - } - - bool Statement::GetColumnIsNull(int column) - { - int type = sqlite3_column_type(m_stmt.get(), column); - return type == SQLITE_NULL; - } - - void Statement::Reset() - { - AICLI_LOG(SQL, Verbose, << "Reset statement #" << m_connectionId << '-' << m_id); - // Ignore return value from reset, as if it is an error, it was the error from the last call to step. - sqlite3_reset(m_stmt.get()); - m_state = State::Prepared; - } - - Savepoint::Savepoint(Connection& connection, std::string&& name) : - m_name(std::move(name)) - { - using namespace std::string_literals; - - Statement begin = Statement::Create(connection, "SAVEPOINT ["s + m_name + "]"); - m_rollbackTo = Statement::Create(connection, "ROLLBACK TO ["s + m_name + "]"); - m_release = Statement::Create(connection, "RELEASE ["s + m_name + "]"); - - AICLI_LOG(SQL, Verbose, << "Begin savepoint: " << m_name); - begin.Step(); - } - - Savepoint Savepoint::Create(Connection& connection, std::string name) - { - return { connection, std::move(name) }; - } - - Savepoint::~Savepoint() - { - Rollback(); - } - - void Savepoint::Rollback() - { - if (m_inProgress) - { - AICLI_LOG(SQL, Verbose, << "Roll back savepoint: " << m_name); - m_rollbackTo.Step(true); - // 'ROLLBACK TO' *DOES NOT* remove the savepoint from the transaction stack. - // In order to remove it, we must RELEASE. Since we just invoked a ROLLBACK TO - // this should have the effect of 'committing' nothing. - m_release.Step(true); - m_inProgress = false; - } - } - - void Savepoint::Commit() - { - if (m_inProgress) - { - AICLI_LOG(SQL, Verbose, << "Commit savepoint: " << m_name); - m_release.Step(true); - m_inProgress = false; - } - } - - std::string_view EscapeCharForLike = "'"sv; - - std::string EscapeStringForLike(std::string_view value) - { - constexpr char singleChar = '_'; - constexpr char multiChar = '%'; - char escapeChar = EscapeCharForLike[0]; - - std::string result; - result.reserve(value.length()); - - for (char c : value) - { - if (c == singleChar || c == multiChar || c == escapeChar) - { - result.append(1, escapeChar); - } - result.append(1, c); - } - - return result; - } -} diff --git a/src/AppInstallerRepositoryCore/SQLiteWrapper.h b/src/AppInstallerRepositoryCore/SQLiteWrapper.h @@ -1,325 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#pragma once -#include <wil/result_macros.h> -#include <wil/resource.h> -#include <winsqlite/winsqlite3.h> - -#include <AppInstallerLogging.h> -#include <AppInstallerLanguageUtilities.h> - -#include <string> -#include <string_view> -#include <tuple> -#include <type_traits> -#include <utility> -#include <vector> - -#define SQLITE_MEMORY_DB_CONNECTION_TARGET ":memory:" - -using namespace std::string_view_literals; - -namespace AppInstaller::Repository::SQLite -{ - // The name of the rowid column in SQLite. - extern std::string_view RowIDName; - - // The type of a rowid column in code. - using rowid_t = int64_t; - - // The type to use for blob data. - using blob_t = std::vector<uint8_t>; - - namespace details - { - template<typename> - constexpr bool dependent_false = false; - - template <typename T, typename = void> - struct ParameterSpecificsImpl - { - static T& ToLog(T&&) - { - static_assert(dependent_false<T>, "No type specific override has been supplied"); - } - static void Bind(sqlite3_stmt*, int, T&&) - { - static_assert(dependent_false<T>, "No type specific override has been supplied"); - } - static T GetColumn(sqlite3_stmt*, int) - { - static_assert(dependent_false<T>, "No type specific override has been supplied"); - } - }; - - template <> - struct ParameterSpecificsImpl<nullptr_t> - { - inline static std::string_view ToLog(nullptr_t) { return "null"sv; } - static void Bind(sqlite3_stmt* stmt, int index, nullptr_t); - }; - - template <> - struct ParameterSpecificsImpl<std::string> - { - inline static const std::string& ToLog(const std::string& v) { return v; } - static void Bind(sqlite3_stmt* stmt, int index, const std::string& v); - static std::string GetColumn(sqlite3_stmt* stmt, int column); - }; - - template <> - struct ParameterSpecificsImpl<std::string_view> - { - inline static const std::string_view& ToLog(const std::string_view& v) { return v; } - static void Bind(sqlite3_stmt* stmt, int index, std::string_view v); - }; - - template <> - struct ParameterSpecificsImpl<int> - { - inline static int ToLog(int v) { return v; } - static void Bind(sqlite3_stmt* stmt, int index, int v); - static int GetColumn(sqlite3_stmt* stmt, int column); - }; - - template <> - struct ParameterSpecificsImpl<int64_t> - { - inline static int64_t ToLog(int64_t v) { return v; } - static void Bind(sqlite3_stmt* stmt, int index, int64_t v); - static int64_t GetColumn(sqlite3_stmt* stmt, int column); - }; - - template <> - struct ParameterSpecificsImpl<bool> - { - inline static bool ToLog(bool v) { return v; } - static void Bind(sqlite3_stmt* stmt, int index, bool v); - static bool GetColumn(sqlite3_stmt* stmt, int column); - }; - - template <> - struct ParameterSpecificsImpl<blob_t> - { - static std::string ToLog(const blob_t& v); - static void Bind(sqlite3_stmt* stmt, int index, const blob_t& v); - static blob_t GetColumn(sqlite3_stmt* stmt, int column); - }; - - template <typename E> - struct ParameterSpecificsImpl<E, typename std::enable_if_t<std::is_enum_v<E>>> - { - static auto ToLog(E v) - { - return ToIntegral(v); - } - static void Bind(sqlite3_stmt* stmt, int index, E v) - { - ParameterSpecificsImpl<std::underlying_type_t<E>>::Bind(stmt, index, ToIntegral(v)); - } - static E GetColumn(sqlite3_stmt* stmt, int column) - { - return ToEnum<E>(ParameterSpecificsImpl<std::underlying_type_t<E>>::GetColumn(stmt, column)); - } - }; - - template <typename T> - using ParameterSpecifics = ParameterSpecificsImpl<std::decay_t<T>>; - } - - // A SQLite exception. - struct SQLiteException : public wil::ResultException - { - SQLiteException(int error) : wil::ResultException(MAKE_HRESULT(SEVERITY_ERROR, FACILITY_SQLITE, error)) {} - }; - - // The connection to a database. - struct Connection - { - // The disposition for opening a database connection. - enum class OpenDisposition : int - { - // Open existing database for reading. - ReadOnly = SQLITE_OPEN_READONLY, - // Open existing database for reading and writing. - ReadWrite = SQLITE_OPEN_READWRITE, - // Create new database for reading and writing. - Create = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, - }; - - // Flags for opening a database connection. - enum class OpenFlags : int - { - // No flags specified. - None = 0, - // Indicate that the target can be a URI. - Uri = SQLITE_OPEN_URI, - }; - - static Connection Create(const std::string& target, OpenDisposition disposition, OpenFlags flags = OpenFlags::None); - - Connection() = default; - - Connection(const Connection&) = delete; - Connection& operator=(const Connection&) = delete; - - Connection(Connection&& other) = default; - Connection& operator=(Connection&& other) = default; - - ~Connection() = default; - - // Enables the ICU integrations on this connection. - void EnableICU(); - - // Gets the last inserted rowid to the database. - rowid_t GetLastInsertRowID(); - - // Gets the count of changed rows for the last executed statement. - int GetChanges() const; - - //. Gets the (fixed but arbitrary) identifier for this connection. - size_t GetID() const; - - operator sqlite3* () const { return m_dbconn.get(); } - - private: - Connection(const std::string& target, OpenDisposition disposition, OpenFlags flags); - - size_t m_id = 0; - wil::unique_any<sqlite3*, decltype(sqlite3_close_v2), sqlite3_close_v2> m_dbconn; - }; - - // A SQL statement. - struct Statement - { - static Statement Create(const Connection& connection, const std::string& sql); - static Statement Create(const Connection& connection, std::string_view sql); - static Statement Create(const Connection& connection, char const* const sql); - - Statement() = default; - - Statement(const Statement&) = delete; - Statement& operator=(const Statement&) = delete; - - Statement(Statement&& other) = default; - Statement& operator=(Statement&& other) = default; - - operator sqlite3_stmt* () const { return m_stmt.get(); } - - // The state of the statement. - enum class State - { - // The statement has been prepared, but not evaluated. - Prepared = 0, - // The statement has a row available for reading. - HasRow = 1, - // The statement has been completed. - Completed = 2, - // The statement has resulted in an error. - Error = 3, - }; - - // Gets the current state of the statement. - State GetState() const { return m_state; } - - // Bind parameters to the statement. - // The index is 1 based. - template <typename Value> - void Bind(int index, Value&& v) - { - AICLI_LOG(SQL, Verbose, << "Binding statement #" << m_connectionId << '-' << m_id << ": " << index << " => " << details::ParameterSpecifics<Value>::ToLog(std::forward<Value>(v))); - details::ParameterSpecifics<Value>::Bind(m_stmt.get(), index, std::forward<Value>(v)); - } - - // Evaluate the statement; either retrieving the next row or executing some action. - // Returns true if there is a row of data, or false if there is none. - // This return value is the equivalent of 'GetState() == State::HasRow' after calling Step. - bool Step(bool failFastOnError = false); - - // Equivalent to Step, but does not ever expect a result, throwing if one is retrieved. - void Execute(bool failFastOnError = false); - - // Gets a boolean value that indicates whether the specified column value is null in the current row. - // The index is 0 based. - bool GetColumnIsNull(int column); - - // Gets the value of the specified column from the current row. - // The index is 0 based. - template <typename Value> - Value GetColumn(int column) - { - THROW_HR_IF(E_BOUNDS, m_state != State::HasRow); - return details::ParameterSpecifics<Value>::GetColumn(m_stmt.get(), column); - } - - // Gets the entire row of values from the current row. - // The values requested *must* be those available starting from the first column, but trailing columns can be omitted. - template <typename... Values> - std::tuple<Values...> GetRow() - { - return GetRowImpl<Values...>(std::make_integer_sequence<int, sizeof...(Values)>{}); - } - - // Resets the statement state, allowing it to be evaluated again. - // Note that this does not clear data bindings. - void Reset(); - - // Determines if the statement owns an underlying object. - operator bool() const { return static_cast<bool>(m_stmt); } - - private: - Statement(const Connection& connection, std::string_view sql); - - // Helper to receive the integer sequence from the public function. - // This is equivalent to calling: - // for (i = 0 .. count of Values types) - // GetColumn<current Value type>(i) - // Then putting them all into a tuple. - template <typename... Values, int... I> - std::tuple<Values...> GetRowImpl(std::integer_sequence<int, I...>) - { - THROW_HR_IF(E_BOUNDS, m_state != State::HasRow); - return std::make_tuple(details::ParameterSpecifics<Values>::GetColumn(m_stmt.get(), I)...); - } - - size_t m_connectionId = 0; - size_t m_id = 0; - wil::unique_any<sqlite3_stmt*, decltype(sqlite3_finalize), sqlite3_finalize> m_stmt; - State m_state = State::Prepared; - }; - - // A SQLite savepoint. - struct Savepoint - { - // Creates a savepoint, beginning it. - static Savepoint Create(Connection& connection, std::string name); - - Savepoint(const Savepoint&) = delete; - Savepoint& operator=(const Savepoint&) = delete; - - Savepoint(Savepoint&&) = default; - Savepoint& operator=(Savepoint&&) = default; - - ~Savepoint(); - - // Rolls back the Savepoint. - void Rollback(); - - // Commits the Savepoint. - void Commit(); - - private: - Savepoint(Connection& connection, std::string&& name); - - std::string m_name; - DestructionToken m_inProgress = true; - Statement m_rollbackTo; - Statement m_release; - }; - - // The escape character used in the EscapeStringForLike function. - extern std::string_view EscapeCharForLike; - - // Escapes the given input string for passing to a like operation. - std::string EscapeStringForLike(std::string_view value); -} diff --git a/src/AppInstallerSharedLib/AppInstallerSharedLib.vcxproj b/src/AppInstallerSharedLib/AppInstallerSharedLib.vcxproj @@ -388,6 +388,7 @@ </ClCompile> </ItemDefinitionGroup> <ItemGroup> + <ClInclude Include="ICU\SQLiteICU.h" /> <ClInclude Include="pch.h" /> <ClInclude Include="Public\AppInstallerDateTime.h" /> <ClInclude Include="Public\AppInstallerErrors.h" /> @@ -406,11 +407,18 @@ <ClInclude Include="Public\winget\JsonSchemaValidation.h" /> <ClInclude Include="Public\winget\JsonUtil.h" /> <ClInclude Include="Public\winget\LocIndependent.h" /> + <ClInclude Include="Public\winget\ManagedFile.h" /> <ClInclude Include="Public\winget\Registry.h" /> <ClInclude Include="Public\winget\Resources.h" /> <ClInclude Include="Public\winget\Runtime.h" /> <ClInclude Include="Public\winget\Security.h" /> <ClInclude Include="Public\winget\SharedThreadGlobals.h" /> + <ClInclude Include="Public\winget\SQLiteMetadataTable.h" /> + <ClInclude Include="Public\winget\SQLiteStatementBuilder.h" /> + <ClInclude Include="Public\winget\SQLiteStorageBase.h" /> + <ClInclude Include="Public\winget\SQLiteTempTable.h" /> + <ClInclude Include="Public\winget\SQLiteVersion.h" /> + <ClInclude Include="Public\winget\SQLiteWrapper.h" /> <ClInclude Include="Public\winget\Yaml.h" /> <ClInclude Include="YamlWrapper.h" /> </ItemGroup> @@ -421,8 +429,25 @@ <ClCompile Include="DateTime.cpp" /> <ClCompile Include="Errors.cpp" /> <ClCompile Include="GroupPolicy.cpp" /> + <ClCompile Include="ICU\SQLiteICU.c"> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Fuzzing|x64'">NotUsing</PrecompiledHeader> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='ReleaseStatic|Win32'">NotUsing</PrecompiledHeader> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">NotUsing</PrecompiledHeader> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">NotUsing</PrecompiledHeader> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='ReleaseStatic|ARM64'">NotUsing</PrecompiledHeader> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">NotUsing</PrecompiledHeader> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">NotUsing</PrecompiledHeader> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='ReleaseStatic|ARM'">NotUsing</PrecompiledHeader> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='ReleaseStatic|x64'">NotUsing</PrecompiledHeader> + </ClCompile> <ClCompile Include="JsonSchemaValidation.cpp" /> <ClCompile Include="JsonUtil.cpp" /> + <ClCompile Include="ManagedFile.cpp" /> + <ClCompile Include="SQLiteMetadataTable.cpp" /> <ClCompile Include="Registry.cpp" /> <ClCompile Include="Resources.cpp" /> <ClCompile Include="pch.cpp"> @@ -432,6 +457,11 @@ <ClCompile Include="Security.cpp" /> <ClCompile Include="SHA256.cpp" /> <ClCompile Include="SharedThreadGlobals.cpp" /> + <ClCompile Include="SQLiteStatementBuilder.cpp" /> + <ClCompile Include="SQLiteStorageBase.cpp" /> + <ClCompile Include="SQLiteTempTable.cpp" /> + <ClCompile Include="SQLiteVersion.cpp" /> + <ClCompile Include="SQLiteWrapper.cpp" /> <ClCompile Include="Versions.cpp" /> <ClCompile Include="Yaml.cpp" /> <ClCompile Include="YamlWrapper.cpp" /> diff --git a/src/AppInstallerSharedLib/AppInstallerSharedLib.vcxproj.filters b/src/AppInstallerSharedLib/AppInstallerSharedLib.vcxproj.filters @@ -18,6 +18,12 @@ <Filter Include="Public\Telemetry - Do Not Modify"> <UniqueIdentifier>{3a5b2424-6c80-4edc-85bc-f371f2e93a33}</UniqueIdentifier> </Filter> + <Filter Include="SQLite"> + <UniqueIdentifier>{d5b3a812-cc25-4826-b380-0488cd0944e1}</UniqueIdentifier> + </Filter> + <Filter Include="ICU"> + <UniqueIdentifier>{2403870a-5bb9-461f-8558-877c23ec487b}</UniqueIdentifier> + </Filter> </ItemGroup> <ItemGroup> <ClInclude Include="pch.h"> @@ -95,6 +101,30 @@ <ClInclude Include="Public\winget\JsonUtil.h"> <Filter>Public\winget</Filter> </ClInclude> + <ClInclude Include="Public\winget\SQLiteStatementBuilder.h"> + <Filter>Public\winget</Filter> + </ClInclude> + <ClInclude Include="Public\winget\SQLiteStorageBase.h"> + <Filter>Public\winget</Filter> + </ClInclude> + <ClInclude Include="Public\winget\SQLiteTempTable.h"> + <Filter>Public\winget</Filter> + </ClInclude> + <ClInclude Include="Public\winget\SQLiteWrapper.h"> + <Filter>Public\winget</Filter> + </ClInclude> + <ClInclude Include="ICU\SQLiteICU.h"> + <Filter>ICU</Filter> + </ClInclude> + <ClInclude Include="Public\winget\SQLiteVersion.h"> + <Filter>Public\winget</Filter> + </ClInclude> + <ClInclude Include="Public\winget\ManagedFile.h"> + <Filter>Public\winget</Filter> + </ClInclude> + <ClInclude Include="Public\winget\SQLiteMetadataTable.h"> + <Filter>Public\winget</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ClCompile Include="pch.cpp"> @@ -151,6 +181,30 @@ <ClCompile Include="JsonUtil.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="SQLiteStatementBuilder.cpp"> + <Filter>SQLite</Filter> + </ClCompile> + <ClCompile Include="SQLiteStorageBase.cpp"> + <Filter>SQLite</Filter> + </ClCompile> + <ClCompile Include="SQLiteTempTable.cpp"> + <Filter>SQLite</Filter> + </ClCompile> + <ClCompile Include="SQLiteWrapper.cpp"> + <Filter>SQLite</Filter> + </ClCompile> + <ClCompile Include="SQLiteMetadataTable.cpp"> + <Filter>SQLite</Filter> + </ClCompile> + <ClCompile Include="ICU\SQLiteICU.c"> + <Filter>ICU</Filter> + </ClCompile> + <ClCompile Include="SQLiteVersion.cpp"> + <Filter>SQLite</Filter> + </ClCompile> + <ClCompile Include="ManagedFile.cpp"> + <Filter>Source Files</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <None Include="PropertySheet.props" /> diff --git a/src/AppInstallerRepositoryCore/ICU/SQLiteICU.c b/src/AppInstallerSharedLib/ICU/SQLiteICU.c diff --git a/src/AppInstallerRepositoryCore/ICU/SQLiteICU.h b/src/AppInstallerSharedLib/ICU/SQLiteICU.h diff --git a/src/AppInstallerCommonCore/ManagedFile.cpp b/src/AppInstallerSharedLib/ManagedFile.cpp diff --git a/src/AppInstallerCommonCore/Public/winget/ManagedFile.h b/src/AppInstallerSharedLib/Public/winget/ManagedFile.h diff --git a/src/AppInstallerSharedLib/Public/winget/SQLiteMetadataTable.h b/src/AppInstallerSharedLib/Public/winget/SQLiteMetadataTable.h @@ -0,0 +1,66 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include <winget/SQLiteWrapper.h> + +#include <wil/result_macros.h> +#include <string_view> + +namespace AppInstaller::SQLite +{ + using namespace std::string_view_literals; + + static constexpr std::string_view s_MetadataValueName_MajorVersion = "majorVersion"sv; + static constexpr std::string_view s_MetadataValueName_MinorVersion = "minorVersion"sv; + static constexpr std::string_view s_MetadataValueName_LastWriteTime = "lastwritetime"sv; + + // The metadata table for the database. + // Contains a fixed-schema set of named values that can be used to determine how to read the rest of the database. + struct MetadataTable + { + static void Create(Connection& connection); + + // Gets the named value from the metadata table, interpreting it as the given type. + template <typename Value> + static Value GetNamedValue(const Connection& connection, std::string_view name) + { + Statement statement = GetNamedValueStatement(connection, name); + return statement.GetColumn<Value>(0); + } + + // Gets the named value from the metadata table, interpreting it as the given type. + // Returns nullopt if the value is not present. + template <typename Value> + static std::optional<Value> TryGetNamedValue(const Connection& connection, std::string_view name) + { + std::optional<Statement> statement = TryGetNamedValueStatement(connection, name); + if (statement) + { + return statement->GetColumn<Value>(0); + } + else + { + return std::nullopt; + } + } + + // Sets the named value into the metadata table. + template <typename Value> + static void SetNamedValue(const Connection& connection, std::string_view name, Value&& v) + { + Statement statement = SetNamedValueStatement(connection, name); + statement.Bind(2, std::forward<Value>(v)); + statement.Execute(); + } + + private: + // Internal function that gets the named value. + static Statement GetNamedValueStatement(const Connection& connection, std::string_view name); + + // Internal function that gets the named value, or nullopt if it is not present. + static std::optional<Statement> TryGetNamedValueStatement(const Connection& connection, std::string_view name); + + // Internal function that sets the named value. + static Statement SetNamedValueStatement(const Connection& connection, std::string_view name); + }; +} diff --git a/src/AppInstallerSharedLib/Public/winget/SQLiteStatementBuilder.h b/src/AppInstallerSharedLib/Public/winget/SQLiteStatementBuilder.h @@ -0,0 +1,499 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include <winget/SQLiteWrapper.h> +#include <AppInstallerLanguageUtilities.h> + +#include <functional> +#include <initializer_list> +#include <memory> +#include <optional> +#include <sstream> +#include <string_view> +#include <vector> + +using namespace std::string_view_literals; + +namespace AppInstaller::SQLite::Builder +{ + namespace details + { + // Sentinel types to indicate special cases to the builder. + struct unbound_t {}; + struct rowcount_t {}; + + // Class for intake from external functions. + struct SubBuilder + { + SubBuilder(std::string&& s) : m_string(std::move(s)) {} + + SubBuilder(const SubBuilder&) = default; + SubBuilder& operator=(const SubBuilder&) = default; + + SubBuilder(SubBuilder&&) noexcept = default; + SubBuilder& operator=(SubBuilder&&) noexcept = default; + + const std::string& GetString() const { return m_string; } + + protected: + std::string m_string; + }; + + // Base class for all sub-builders. + struct SubBuilderBase + { + SubBuilderBase() = default; + + SubBuilderBase(const SubBuilderBase&) = default; + SubBuilderBase& operator=(const SubBuilderBase&) = default; + + SubBuilderBase(SubBuilderBase&&) noexcept = default; + SubBuilderBase& operator=(SubBuilderBase&&) noexcept = default; + + virtual operator SubBuilder() { return { m_stream.str() }; } + + protected: + std::ostringstream m_stream; + }; + } + + // Pass this value to indicate that the caller will bind the value later. + __declspec_selectany_ details::unbound_t Unbound; + + // Pass this value to indicate that the number of rows is to be selected. + __declspec_selectany_ details::rowcount_t RowCount; + + // A qualified table reference. + struct QualifiedTable + { + std::string_view Schema; + std::string_view Table; + + explicit constexpr QualifiedTable(std::string_view table) : Table(table) {} + explicit constexpr QualifiedTable(std::string_view schema, std::string_view table) : Schema(schema), Table(table) {} + }; + + namespace Schema + { + // The main database's schema table. + // More info can be found at: https://www.sqlite.org/schematab.html + constexpr QualifiedTable MainTable{ "main"sv, "sqlite_master"sv }; + + // The sqlite_schema column name for the type of the object. + constexpr std::string_view TypeColumn = "type"sv; + + // The sqlite_schema type value for a table. + constexpr std::string_view Type_Table = "table"sv; + + // The sqlite_schema type value for an index. + constexpr std::string_view Type_Index = "index"sv; + + // The sqlite_schema column name for the name of the object. + constexpr std::string_view NameColumn = "name"sv; + } + + // A qualified column reference. + struct QualifiedColumn + { + std::string_view Table; + std::string_view Column; + + explicit QualifiedColumn(std::string_view column) : Column(column) {} + explicit QualifiedColumn(std::string_view table, std::string_view column) : Table(table), Column(column) {} + }; + + // SQLite types as an enum. + enum class Type + { + Int, + Bool = Int, + Int64, + RowId = Int64, + Text, + Blob, + Integer, // Type for specifying a primary key column as a row id alias. + }; + + // Aggregate functions. + enum class Aggregate + { + Min, + Max, + }; + + // 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 + { + // Specify the column name and type when creating the builder. + ColumnBuilder(std::string_view column, Type type); + + ColumnBuilder(const ColumnBuilder&) = default; + ColumnBuilder& operator=(const ColumnBuilder&) = default; + + ColumnBuilder(ColumnBuilder&&) noexcept = default; + ColumnBuilder& operator=(ColumnBuilder&&) noexcept = default; + + // Indicate that the column is not able to be null. + // Allow for data driven construction with input value. + ColumnBuilder& NotNull(bool isTrue = true); + + // Indicate that the column is case-insensitive. + // Allow for data driven construction with input value. + ColumnBuilder& CollateNoCase(bool isTrue = true); + + // Indicate the default value for the column. + // Note that a default value is not considered constant if it is bound, + // so this function directly places the incoming value into the SQL statement. + ColumnBuilder& Default(int64_t value); + + // Indicate that the column is unique. + // Allow for data driven construction with input value. + ColumnBuilder& Unique(bool isTrue = true); + + // Indicate that the column is the primary key. + // Allow for data driven construction with input value. + ColumnBuilder& PrimaryKey(bool isTrue = true); + }; + + // Helper used to specify a primary key with multiple columns. + struct PrimaryKeyBuilder : public details::SubBuilderBase + { + PrimaryKeyBuilder(); + PrimaryKeyBuilder(std::initializer_list<std::string_view> columns); + + PrimaryKeyBuilder(const PrimaryKeyBuilder&) = default; + PrimaryKeyBuilder& operator=(const PrimaryKeyBuilder&) = default; + + PrimaryKeyBuilder(PrimaryKeyBuilder&&) noexcept = default; + PrimaryKeyBuilder& operator=(PrimaryKeyBuilder&&) noexcept = default; + + virtual operator details::SubBuilder() override; + + // Add a column to the primary key. + PrimaryKeyBuilder& Column(std::string_view column); + + private: + bool m_isFirst = true; + bool m_needsClosing = true; + }; + + // A class that aids in building SQL statements in a more expressive manner than simple strings. + struct StatementBuilder + { + StatementBuilder() = default; + + StatementBuilder(const StatementBuilder&) = default; + StatementBuilder& operator=(const StatementBuilder&) = default; + + StatementBuilder(StatementBuilder&&) = default; + StatementBuilder& operator=(StatementBuilder&&) = default; + + // Begin a select statement for the given columns. + StatementBuilder& Select(); + StatementBuilder& Select(std::string_view column); + StatementBuilder& Select(std::initializer_list<std::string_view> columns); + StatementBuilder& Select(const QualifiedColumn& column); + StatementBuilder& Select(std::initializer_list<QualifiedColumn> columns); + StatementBuilder& Select(details::rowcount_t); + + // Indicate the table that the statement will be operating on. + // The initializer_list form enables the table name to be constructed from multiple parts. + StatementBuilder& From(); + StatementBuilder& From(std::string_view table); + StatementBuilder& From(QualifiedTable table); + StatementBuilder& From(std::initializer_list<std::string_view> table); + + // Begin a filter clause on the given column. + StatementBuilder& Where(std::string_view column); + StatementBuilder& Where(const QualifiedColumn& column); + + // A full filter clause looking for an embedded null character. + // Is extremely specific to consistency checks, and so a more detailed construct is not required. + StatementBuilder& WhereValueContainsEmbeddedNullCharacter(std::string_view column); + StatementBuilder& WhereValueContainsEmbeddedNullCharacter(const QualifiedColumn& column); + + // Indicate the operation of the filter clause. + template <typename ValueType> + StatementBuilder& Equals(const ValueType& value) + { + AddBindFunctor(AppendOpAndBinder(Op::Equals), value); + return *this; + } + template <typename ValueType> + StatementBuilder& Equals(const std::optional<ValueType>& value) + { + if (value) + { + AddBindFunctor(AppendOpAndBinder(Op::Equals), value.value()); + return *this; + } + else + { + return IsNull(); + } + } + // The optional index value can be used to specify the parameter index. + StatementBuilder& Equals(details::unbound_t, std::optional<size_t> index = {}); + StatementBuilder& Equals(std::nullptr_t); + StatementBuilder& Equals(); + + StatementBuilder& LikeWithEscape(std::string_view value); + StatementBuilder& Like(details::unbound_t); + + StatementBuilder& LiteralColumn(std::string_view value); + + StatementBuilder& Escape(std::string_view escapeChar); + + StatementBuilder& Not(); + StatementBuilder& In(); + + // Appends a set of value binders for the In clause. + StatementBuilder& In(size_t count); + + // IsNull(true) means the value is null; IsNull(false) means the value is not null. + StatementBuilder& IsNull(bool isNull = true); + StatementBuilder& IsNotNull() { return IsNull(false); } + + // Operators for combining filter clauses. + StatementBuilder& And(std::string_view column); + StatementBuilder& And(const QualifiedColumn& column); + StatementBuilder& Or(const QualifiedColumn& column); + + // Begin a join clause. + // The initializer_list form enables the table name to be constructed from multiple parts. + StatementBuilder& Join(std::string_view table); + StatementBuilder& Join(QualifiedTable table); + StatementBuilder& Join(std::initializer_list<std::string_view> table); + + // Begin a left outer join clause. + // The initializer_list form enables the table name to be constructed from multiple parts. + StatementBuilder& LeftOuterJoin(std::string_view table); + StatementBuilder& LeftOuterJoin(QualifiedTable table); + StatementBuilder& LeftOuterJoin(std::initializer_list<std::string_view> table); + + // Set the join constraint. + StatementBuilder& On(const QualifiedColumn& column1, const QualifiedColumn& column2); + + // Specify the grouping to use. + StatementBuilder& GroupBy(std::string_view column); + StatementBuilder& GroupBy(const QualifiedColumn& column); + + // Specify the ordering to use. + StatementBuilder& OrderBy(std::string_view column); + StatementBuilder& OrderBy(const QualifiedColumn& column); + + // Specify the ordering behavior. + StatementBuilder& Ascending(); + StatementBuilder& Descending(); + + // Limits the result set to the given number of rows. + StatementBuilder& Limit(size_t rowCount); + + // Begin an insert statement for the given table. + // The initializer_list form enables the table name to be constructed from multiple parts. + StatementBuilder& InsertInto(std::string_view table); + StatementBuilder& InsertInto(QualifiedTable table); + StatementBuilder& InsertInto(std::initializer_list<std::string_view> table); + + // Set the columns for a statement (typically insert). + StatementBuilder& Columns(std::string_view column); + StatementBuilder& Columns(std::initializer_list<std::string_view> columns); + StatementBuilder& Columns(const QualifiedColumn& column); + StatementBuilder& Columns(std::initializer_list<QualifiedColumn> columns); + + // Set the columns for a select or create table statement. + StatementBuilder& Columns(std::initializer_list<details::SubBuilder> columns); + StatementBuilder& BeginColumns(); + StatementBuilder& Column(std::string_view column); + StatementBuilder& Column(const QualifiedColumn& column); + StatementBuilder& Column(Aggregate aggOp, std::string_view column); + StatementBuilder& Column(Aggregate aggOp, const QualifiedColumn& column); + StatementBuilder& Column(const details::SubBuilder& column); + StatementBuilder& EndColumns(); + + // Add the values clause for an insert statement. + template <typename... ValueTypes> + StatementBuilder& Values(const ValueTypes&... values) + { + int bindIndexBegin = AppendValuesAndBinders(sizeof...(ValueTypes)); + // Use folding to add a binder for every value, specifically in the order they were given. + // Do not change this expression without understanding the implications to the bind order. + // See: https://en.cppreference.com/w/cpp/language/fold for more details. + (FoldHelper{}, ..., InsertValuesValueBinder(bindIndexBegin++, values)); + return *this; + } + StatementBuilder& BeginValues(); + template <typename ValueType> + StatementBuilder& Value(const ValueType& value) + { + InsertValuesValueBinder(AppendValueAndBinder(), value); + return *this; + } + StatementBuilder& EndValues(); + + // Begin a table creation statement. + // The initializer_list form enables the table name to be constructed from multiple parts. + StatementBuilder& CreateTable(std::string_view table); + StatementBuilder& CreateTable(QualifiedTable table); + StatementBuilder& CreateTable(std::initializer_list<std::string_view> table); + + // Begin an alter table statement. + // The initializer_list form enables the table name to be constructed from multiple parts. + StatementBuilder& AlterTable(std::string_view table); + StatementBuilder& AlterTable(QualifiedTable table); + StatementBuilder& AlterTable(std::initializer_list<std::string_view> table); + + // Complete an alter table statement by adding a column. + StatementBuilder& Add(std::string_view column, Type type); + + // Begin an table deletion statement. + // The initializer_list form enables the table name to be constructed from multiple parts. + StatementBuilder& DropTable(std::string_view table); + StatementBuilder& DropTable(QualifiedTable table); + StatementBuilder& DropTable(std::initializer_list<std::string_view> table); + + // Begin an index creation statement. + // The initializer_list form enables the index name to be constructed from multiple parts. + StatementBuilder& CreateIndex(std::string_view table); + StatementBuilder& CreateIndex(QualifiedTable table); + StatementBuilder& CreateIndex(std::initializer_list<std::string_view> table); + + // Begin an unique index creation statement. + // The initializer_list form enables the index name to be constructed from multiple parts. + StatementBuilder& CreateUniqueIndex(std::string_view table); + StatementBuilder& CreateUniqueIndex(QualifiedTable table); + StatementBuilder& CreateUniqueIndex(std::initializer_list<std::string_view> table); + + // Begin an index deletion statement. + // The initializer_list form enables the table name to be constructed from multiple parts. + StatementBuilder& DropIndex(std::string_view index); + StatementBuilder& DropIndex(QualifiedTable index); + StatementBuilder& DropIndex(std::initializer_list<std::string_view> index); + + // Set index target table. + StatementBuilder& On(std::string_view table); + StatementBuilder& On(std::initializer_list<std::string_view> table); + + // Begin a delete statement. + // The initializer_list form enables the table name to be constructed from multiple parts. + StatementBuilder& DeleteFrom(std::string_view table); + StatementBuilder& DeleteFrom(QualifiedTable table); + StatementBuilder& DeleteFrom(std::initializer_list<std::string_view> table); + + // Begin an update statement. + // The initializer_list form enables the table name to be constructed from multiple parts. + StatementBuilder& Update(std::string_view table); + StatementBuilder& Update(QualifiedTable table); + StatementBuilder& Update(std::initializer_list<std::string_view> table); + + // Begin an `update or replace` statement. + // The initializer_list form enables the table name to be constructed from multiple parts. + StatementBuilder& UpdateOrReplace(std::string_view table); + StatementBuilder& UpdateOrReplace(QualifiedTable table); + StatementBuilder& UpdateOrReplace(std::initializer_list<std::string_view> table); + + // Output the set portion of an update statement. + StatementBuilder& Set(); + + // Output the set portion of an update statement. + StatementBuilder& Vacuum(); + + // General purpose functions to begin and end a parenthetical expression. + StatementBuilder& BeginParenthetical(); + StatementBuilder& EndParenthetical(); + + // Adds the `without rowid` clause. + StatementBuilder& WithoutRowID(); + + // Assign an alias to the previous item. + StatementBuilder& As(std::string_view alias); + + // Gets the last bound index. + // A value of zero indicates that nothing has been bound. + int GetLastBindIndex() const { return m_bindIndex - 1; } + + // Prepares and returns the statement, applying any bindings that were requested. + Statement Prepare(const Connection& connection); + + // A convenience function that prepares, binds, and then executes a statement that does not return rows. + void Execute(const Connection& connection); + + private: + enum class Op + { + Equals, + Like, + Escape, + Literal, + }; + + // Appends given the operation. + // The optional index value can be used to specify the parameter index. + int AppendOpAndBinder(Op op, std::optional<size_t> index = {}); + + // Appends a set of binders for the values clause of an insert. + int AppendValuesAndBinders(size_t count); + + // Appends a binder for the values clause of an insert. + int AppendValueAndBinder(); + + // Adds a functor to our list that will bind the given value. + template <typename ValueType> + void AddBindFunctor(int binderIndex, const ValueType& value) + { + m_binders.emplace_back([binderIndex, value](Statement& s) { s.Bind(binderIndex, value); }); + } + + // Helper template for binding incoming values for an insert. + template <typename ValueType> + StatementBuilder& InsertValuesValueBinder(int bindIndex, const ValueType& value) + { + AddBindFunctor(bindIndex, value); + return *this; + } + template <typename ValueType> + StatementBuilder& InsertValuesValueBinder(int bindIndex, const std::optional<ValueType>& value) + { + if (value) + { + AddBindFunctor(bindIndex, value.value()); + } + else + { + AddBindFunctor(bindIndex, nullptr); + } + return *this; + } + StatementBuilder& InsertValuesValueBinder(int, details::unbound_t) + { + return *this; + } + StatementBuilder& InsertValuesValueBinder(int bindIndex, std::nullptr_t) + { + AddBindFunctor(bindIndex, nullptr); + return *this; + } + + std::ostringstream m_stream; + // Because binding values starts at 1 + int m_bindIndex = 1; + std::vector<std::function<void(Statement&)>> m_binders; + bool m_needsComma = false; + }; +} diff --git a/src/AppInstallerSharedLib/Public/winget/SQLiteStorageBase.h b/src/AppInstallerSharedLib/Public/winget/SQLiteStorageBase.h @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include <winget/SQLiteWrapper.h> +#include <winget/SQLiteVersion.h> +#include <winget/ManagedFile.h> + +#include <mutex> + +namespace AppInstaller::SQLite +{ + struct SQLiteStorageBase + { + // The disposition for opening the database. + enum class OpenDisposition + { + // Open for read only. + Read, + // Open for read and write. + ReadWrite, + // The database will not change while in use; open for immutable read. + Immutable, + }; + + // Gets the last write time for the database. + std::chrono::system_clock::time_point GetLastWriteTime(); + + // Gets the schema version of the database. + Version GetVersion() const { return m_version; } + + protected: + SQLiteStorageBase(const std::string& target, const Version& version); + + SQLiteStorageBase(const std::string& filePath, SQLiteStorageBase::OpenDisposition disposition, Utility::ManagedFile&& indexFile); + + // Sets the last write time metadata value in the database. + void SetLastWriteTime(); + + Utility::ManagedFile m_indexFile; + SQLite::Connection m_dbconn; + Version m_version; + std::unique_ptr<std::mutex> m_interfaceLock = std::make_unique<std::mutex>(); + }; +}+ \ No newline at end of file diff --git a/src/AppInstallerSharedLib/Public/winget/SQLiteTempTable.h b/src/AppInstallerSharedLib/Public/winget/SQLiteTempTable.h @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include <winget/SQLiteWrapper.h> +#include <winget/SQLiteStatementBuilder.h> + + +namespace AppInstaller::SQLite +{ + // The base for a class that represents a temp table. + struct TempTable + { + TempTable(); + + ~TempTable(); + + TempTable(const TempTable&) = delete; + TempTable& operator=(const TempTable&) = delete; + + TempTable(TempTable&&) = default; + TempTable& operator=(TempTable&&) = default; + + protected: + // Gets the qualified name of the temp table. + Builder::QualifiedTable GetQualifiedName() const; + + // Prepares the drop table statement for use in destructor. + // It needs to be run by the derived class after the table is actually created. + void InitDropStatement(const Connection& connection); + + private: + std::string m_name; + Statement m_dropTableStatement; + }; +} diff --git a/src/AppInstallerSharedLib/Public/winget/SQLiteVersion.h b/src/AppInstallerSharedLib/Public/winget/SQLiteVersion.h @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include <winget/SQLiteWrapper.h> +#include <memory> + +namespace AppInstaller::SQLite +{ + // Represents the schema version of the database. + struct Version + { + // The major version of the schema. + // All minor changes to this major version must be backward compatible. + uint32_t MajorVersion{}; + // The minor version of the schema. + // All changes to the schema warrant a change to the minor version. + uint32_t MinorVersion{}; + + bool operator==(const Version& other) const + { + return (MajorVersion == other.MajorVersion && MinorVersion == other.MinorVersion); + } + + bool operator!=(const Version& other) const + { + return !operator==(other); + } + + bool operator>=(const Version& other) const + { + if (MajorVersion > other.MajorVersion) return true; + if (MajorVersion < other.MajorVersion) return false; + return MinorVersion >= other.MinorVersion; + } + + // Gets a version that represents the latest schema known to the implementation. + static Version Latest(); + + // Gets a version that represents the latest schema known to the implementation for the given major version. + static Version LatestForMajor(uint32_t majorVersion); + + // Determines if this version represents the latest schema. + bool IsLatest() const; + + // Determines if this version represents the latest schema of the given major version. + bool IsLatestForMajor(uint32_t majorVersion) const; + + // Determines the schema version of the opened database. + static Version GetSchemaVersion(Connection& connection); + + // Writes the current version to the given database. + void SetSchemaVersion(Connection& connection); + }; + + // Output the version + std::ostream& operator<<(std::ostream& out, const Version& version); +} diff --git a/src/AppInstallerSharedLib/Public/winget/SQLiteWrapper.h b/src/AppInstallerSharedLib/Public/winget/SQLiteWrapper.h @@ -0,0 +1,325 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include <wil/result_macros.h> +#include <wil/resource.h> +#include <winsqlite/winsqlite3.h> + +#include <AppInstallerLogging.h> +#include <AppInstallerLanguageUtilities.h> + +#include <string> +#include <string_view> +#include <tuple> +#include <type_traits> +#include <utility> +#include <vector> + +#define SQLITE_MEMORY_DB_CONNECTION_TARGET ":memory:" + +using namespace std::string_view_literals; + +namespace AppInstaller::SQLite +{ + // The name of the rowid column in SQLite. + extern std::string_view RowIDName; + + // The type of a rowid column in code. + using rowid_t = int64_t; + + // The type to use for blob data. + using blob_t = std::vector<uint8_t>; + + namespace details + { + template<typename> + constexpr bool dependent_false = false; + + template <typename T, typename = void> + struct ParameterSpecificsImpl + { + static T& ToLog(T&&) + { + static_assert(dependent_false<T>, "No type specific override has been supplied"); + } + static void Bind(sqlite3_stmt*, int, T&&) + { + static_assert(dependent_false<T>, "No type specific override has been supplied"); + } + static T GetColumn(sqlite3_stmt*, int) + { + static_assert(dependent_false<T>, "No type specific override has been supplied"); + } + }; + + template <> + struct ParameterSpecificsImpl<nullptr_t> + { + inline static std::string_view ToLog(nullptr_t) { return "null"sv; } + static void Bind(sqlite3_stmt* stmt, int index, nullptr_t); + }; + + template <> + struct ParameterSpecificsImpl<std::string> + { + inline static const std::string& ToLog(const std::string& v) { return v; } + static void Bind(sqlite3_stmt* stmt, int index, const std::string& v); + static std::string GetColumn(sqlite3_stmt* stmt, int column); + }; + + template <> + struct ParameterSpecificsImpl<std::string_view> + { + inline static const std::string_view& ToLog(const std::string_view& v) { return v; } + static void Bind(sqlite3_stmt* stmt, int index, std::string_view v); + }; + + template <> + struct ParameterSpecificsImpl<int> + { + inline static int ToLog(int v) { return v; } + static void Bind(sqlite3_stmt* stmt, int index, int v); + static int GetColumn(sqlite3_stmt* stmt, int column); + }; + + template <> + struct ParameterSpecificsImpl<int64_t> + { + inline static int64_t ToLog(int64_t v) { return v; } + static void Bind(sqlite3_stmt* stmt, int index, int64_t v); + static int64_t GetColumn(sqlite3_stmt* stmt, int column); + }; + + template <> + struct ParameterSpecificsImpl<bool> + { + inline static bool ToLog(bool v) { return v; } + static void Bind(sqlite3_stmt* stmt, int index, bool v); + static bool GetColumn(sqlite3_stmt* stmt, int column); + }; + + template <> + struct ParameterSpecificsImpl<blob_t> + { + static std::string ToLog(const blob_t& v); + static void Bind(sqlite3_stmt* stmt, int index, const blob_t& v); + static blob_t GetColumn(sqlite3_stmt* stmt, int column); + }; + + template <typename E> + struct ParameterSpecificsImpl<E, typename std::enable_if_t<std::is_enum_v<E>>> + { + static auto ToLog(E v) + { + return ToIntegral(v); + } + static void Bind(sqlite3_stmt* stmt, int index, E v) + { + ParameterSpecificsImpl<std::underlying_type_t<E>>::Bind(stmt, index, ToIntegral(v)); + } + static E GetColumn(sqlite3_stmt* stmt, int column) + { + return ToEnum<E>(ParameterSpecificsImpl<std::underlying_type_t<E>>::GetColumn(stmt, column)); + } + }; + + template <typename T> + using ParameterSpecifics = ParameterSpecificsImpl<std::decay_t<T>>; + } + + // A SQLite exception. + struct SQLiteException : public wil::ResultException + { + SQLiteException(int error) : wil::ResultException(MAKE_HRESULT(SEVERITY_ERROR, FACILITY_SQLITE, error)) {} + }; + + // The connection to a database. + struct Connection + { + // The disposition for opening a database connection. + enum class OpenDisposition : int + { + // Open existing database for reading. + ReadOnly = SQLITE_OPEN_READONLY, + // Open existing database for reading and writing. + ReadWrite = SQLITE_OPEN_READWRITE, + // Create new database for reading and writing. + Create = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, + }; + + // Flags for opening a database connection. + enum class OpenFlags : int + { + // No flags specified. + None = 0, + // Indicate that the target can be a URI. + Uri = SQLITE_OPEN_URI, + }; + + static Connection Create(const std::string& target, OpenDisposition disposition, OpenFlags flags = OpenFlags::None); + + Connection() = default; + + Connection(const Connection&) = delete; + Connection& operator=(const Connection&) = delete; + + Connection(Connection&& other) = default; + Connection& operator=(Connection&& other) = default; + + ~Connection() = default; + + // Enables the ICU integrations on this connection. + void EnableICU(); + + // Gets the last inserted rowid to the database. + rowid_t GetLastInsertRowID(); + + // Gets the count of changed rows for the last executed statement. + int GetChanges() const; + + //. Gets the (fixed but arbitrary) identifier for this connection. + size_t GetID() const; + + operator sqlite3* () const { return m_dbconn.get(); } + + private: + Connection(const std::string& target, OpenDisposition disposition, OpenFlags flags); + + size_t m_id = 0; + wil::unique_any<sqlite3*, decltype(sqlite3_close_v2), sqlite3_close_v2> m_dbconn; + }; + + // A SQL statement. + struct Statement + { + static Statement Create(const Connection& connection, const std::string& sql); + static Statement Create(const Connection& connection, std::string_view sql); + static Statement Create(const Connection& connection, char const* const sql); + + Statement() = default; + + Statement(const Statement&) = delete; + Statement& operator=(const Statement&) = delete; + + Statement(Statement&& other) = default; + Statement& operator=(Statement&& other) = default; + + operator sqlite3_stmt* () const { return m_stmt.get(); } + + // The state of the statement. + enum class State + { + // The statement has been prepared, but not evaluated. + Prepared = 0, + // The statement has a row available for reading. + HasRow = 1, + // The statement has been completed. + Completed = 2, + // The statement has resulted in an error. + Error = 3, + }; + + // Gets the current state of the statement. + State GetState() const { return m_state; } + + // Bind parameters to the statement. + // The index is 1 based. + template <typename Value> + void Bind(int index, Value&& v) + { + AICLI_LOG(SQL, Verbose, << "Binding statement #" << m_connectionId << '-' << m_id << ": " << index << " => " << details::ParameterSpecifics<Value>::ToLog(std::forward<Value>(v))); + details::ParameterSpecifics<Value>::Bind(m_stmt.get(), index, std::forward<Value>(v)); + } + + // Evaluate the statement; either retrieving the next row or executing some action. + // Returns true if there is a row of data, or false if there is none. + // This return value is the equivalent of 'GetState() == State::HasRow' after calling Step. + bool Step(bool failFastOnError = false); + + // Equivalent to Step, but does not ever expect a result, throwing if one is retrieved. + void Execute(bool failFastOnError = false); + + // Gets a boolean value that indicates whether the specified column value is null in the current row. + // The index is 0 based. + bool GetColumnIsNull(int column); + + // Gets the value of the specified column from the current row. + // The index is 0 based. + template <typename Value> + Value GetColumn(int column) + { + THROW_HR_IF(E_BOUNDS, m_state != State::HasRow); + return details::ParameterSpecifics<Value>::GetColumn(m_stmt.get(), column); + } + + // Gets the entire row of values from the current row. + // The values requested *must* be those available starting from the first column, but trailing columns can be omitted. + template <typename... Values> + std::tuple<Values...> GetRow() + { + return GetRowImpl<Values...>(std::make_integer_sequence<int, sizeof...(Values)>{}); + } + + // Resets the statement state, allowing it to be evaluated again. + // Note that this does not clear data bindings. + void Reset(); + + // Determines if the statement owns an underlying object. + operator bool() const { return static_cast<bool>(m_stmt); } + + private: + Statement(const Connection& connection, std::string_view sql); + + // Helper to receive the integer sequence from the public function. + // This is equivalent to calling: + // for (i = 0 .. count of Values types) + // GetColumn<current Value type>(i) + // Then putting them all into a tuple. + template <typename... Values, int... I> + std::tuple<Values...> GetRowImpl(std::integer_sequence<int, I...>) + { + THROW_HR_IF(E_BOUNDS, m_state != State::HasRow); + return std::make_tuple(details::ParameterSpecifics<Values>::GetColumn(m_stmt.get(), I)...); + } + + size_t m_connectionId = 0; + size_t m_id = 0; + wil::unique_any<sqlite3_stmt*, decltype(sqlite3_finalize), sqlite3_finalize> m_stmt; + State m_state = State::Prepared; + }; + + // A SQLite savepoint. + struct Savepoint + { + // Creates a savepoint, beginning it. + static Savepoint Create(Connection& connection, std::string name); + + Savepoint(const Savepoint&) = delete; + Savepoint& operator=(const Savepoint&) = delete; + + Savepoint(Savepoint&&) = default; + Savepoint& operator=(Savepoint&&) = default; + + ~Savepoint(); + + // Rolls back the Savepoint. + void Rollback(); + + // Commits the Savepoint. + void Commit(); + + private: + Savepoint(Connection& connection, std::string&& name); + + std::string m_name; + DestructionToken m_inProgress = true; + Statement m_rollbackTo; + Statement m_release; + }; + + // The escape character used in the EscapeStringForLike function. + extern std::string_view EscapeCharForLike; + + // Escapes the given input string for passing to a like operation. + std::string EscapeStringForLike(std::string_view value); +} diff --git a/src/AppInstallerSharedLib/SQLiteMetadataTable.cpp b/src/AppInstallerSharedLib/SQLiteMetadataTable.cpp @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "Public/winget/SQLiteMetadataTable.h" + + +using namespace std::literals; + +namespace AppInstaller::SQLite +{ + // Table data [note that this table is not versioned, and thus *cannot change*] + static constexpr std::string_view s_MetadataTable_Table_Name = "metadata"sv; + static constexpr std::string_view s_MetadataTable_Column_Name = "name"sv; + static constexpr std::string_view s_MetadataTable_Column_Value = "value"sv; + + static constexpr std::string_view s_MetadataTable_Table_Create = R"( +CREATE TABLE [metadata]( + [name] TEXT PRIMARY KEY NOT NULL, + [value] TEXT NOT NULL) +)"sv; + + // Statements + static constexpr std::string_view s_MetadataTableStmt_GetNamedValue = "select [value] from [metadata] where [name] = ?"sv; + static constexpr std::string_view s_MetadataTableStmt_SetNamedValue = "insert or replace into [metadata] ([name], [value]) values (?, ?)"sv; + + void MetadataTable::Create(Connection& connection) + { + Statement create = Statement::Create(connection, s_MetadataTable_Table_Create); + create.Execute(); + } + + Statement MetadataTable::GetNamedValueStatement(const Connection& connection, std::string_view name) + { + std::optional<Statement> result = TryGetNamedValueStatement(connection, name); + THROW_HR_IF(E_NOT_SET, !result); + return std::move(result).value(); + } + + std::optional<Statement> MetadataTable::TryGetNamedValueStatement(const Connection& connection, std::string_view name) + { + THROW_HR_IF(E_INVALIDARG, name.empty()); + + Statement result = Statement::Create(connection, s_MetadataTableStmt_GetNamedValue); + result.Bind(1, name); + + if (result.Step()) + { + return result; + } + else + { + return std::nullopt; + } + } + + Statement MetadataTable::SetNamedValueStatement(const Connection& connection, std::string_view name) + { + THROW_HR_IF(E_INVALIDARG, name.empty()); + Statement result = Statement::Create(connection, s_MetadataTableStmt_SetNamedValue); + result.Bind(1, name); + return result; + } +} diff --git a/src/AppInstallerSharedLib/SQLiteStatementBuilder.cpp b/src/AppInstallerSharedLib/SQLiteStatementBuilder.cpp @@ -0,0 +1,944 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "Public/winget/SQLiteStatementBuilder.h" + +namespace AppInstaller::SQLite::Builder +{ + std::ostream& operator<<(std::ostream& out, const QualifiedColumn& column) + { + if (!column.Table.empty()) + { + out << '[' << column.Table << "]."; + } + out << '[' << column.Column << ']'; + return out; + } + + std::ostream& operator<<(std::ostream& out, const QualifiedTable& table) + { + if (!table.Schema.empty()) + { + out << '[' << table.Schema << "]."; + } + out << '[' << table.Table << ']'; + return out; + } + + std::ostream& operator<<(std::ostream& out, const details::SubBuilder& column) + { + out << column.GetString(); + return out; + } + + namespace + { + void OutputColumns(std::ostream& out, std::string_view op, std::string_view column) + { + out << op << '[' << column << ']'; + } + + void OutputColumns(std::ostream& out, std::string_view op, std::initializer_list<std::string_view> columns) + { + out << op; + bool isFirst = true; + for (const auto& c : columns) + { + out << (isFirst ? "[" : ", [") << c << ']'; + isFirst = false; + } + } + + void OutputColumns(std::ostream& out, std::string_view op, const QualifiedColumn& column) + { + out << op << column; + } + + void OutputColumns(std::ostream& out, std::string_view op, std::initializer_list<QualifiedColumn> columns) + { + out << op; + bool isFirst = true; + for (const auto& c : columns) + { + out << (isFirst ? "" : ", ") << c; + isFirst = false; + } + } + + void OutputColumns(std::ostream& out, std::string_view op, std::initializer_list<details::SubBuilder> columns) + { + out << op; + bool isFirst = true; + for (const auto& c : columns) + { + out << (isFirst ? "" : ", ") << c; + isFirst = false; + } + } + + void OutputAggregate(std::ostream& out, Aggregate op) + { + out << ' '; + switch (op) + { + case Aggregate::Min: + out << "MIN"; + break; + case Aggregate::Max: + out << "MAX"; + break; + default: + THROW_HR(E_UNEXPECTED); + } + } + + void OutputColumns(std::ostream& out, Aggregate op, std::string_view column) + { + OutputAggregate(out, op); + out << "([" << column << "])"; + } + + void OutputColumns(std::ostream& out, Aggregate op, const QualifiedColumn& column) + { + OutputAggregate(out, op); + out << '(' << column << ')'; + } + + // Use to output operation and table name, such as " FROM [table]" + void OutputOperationAndTable(std::ostream& out, std::string_view op, std::string_view table) + { + out << op << " [" << table << ']'; + } + + void OutputOperationAndTable(std::ostream& out, std::string_view op, QualifiedTable table) + { + out << op << table; + } + + void OutputOperationAndTable(std::ostream& out, std::string_view op, std::initializer_list<std::string_view> table) + { + out << op << " ["; + for (std::string_view t : table) + { + out << t; + } + out << ']'; + } + + void OutputType(std::ostream& out, Type type) + { + out << ' '; + switch (type) + { + case Type::Int: + out << "INT"; + break; + case Type::Int64: + out << "INT64"; + break; + case Type::Text: + out << "TEXT"; + break; + case Type::Blob: + out << "BLOB"; + break; + case Type::Integer: + out << "INTEGER"; + break; + default: + THROW_HR(E_UNEXPECTED); + } + } + } + + 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); + OutputType(m_stream, type); + } + + ColumnBuilder& ColumnBuilder::NotNull(bool isTrue) + { + if (isTrue) + { + m_stream << " NOT NULL"; + } + return *this; + } + + ColumnBuilder& ColumnBuilder::CollateNoCase(bool isTrue) + { + if (isTrue) + { + m_stream << " COLLATE NOCASE"; + } + return *this; + } + + ColumnBuilder& ColumnBuilder::Default(int64_t value) + { + m_stream << " DEFAULT " << value; + return *this; + } + + ColumnBuilder& ColumnBuilder::Unique(bool isTrue) + { + if (isTrue) + { + m_stream << " UNIQUE"; + } + return *this; + } + + ColumnBuilder& ColumnBuilder::PrimaryKey(bool isTrue) + { + if (isTrue) + { + m_stream << " PRIMARY KEY"; + } + return *this; + } + + PrimaryKeyBuilder::PrimaryKeyBuilder(std::initializer_list<std::string_view> columns) + { + OutputColumns(m_stream, "PRIMARY KEY(", columns); + m_stream << ')'; + m_needsClosing = false; + } + + PrimaryKeyBuilder::PrimaryKeyBuilder() + { + m_stream << "PRIMARY KEY("; + } + + PrimaryKeyBuilder& PrimaryKeyBuilder::Column(std::string_view column) + { + if (m_isFirst) + { + m_isFirst = false; + } + else + { + m_stream << ", "; + } + OutputColumns(m_stream, "", column); + return *this; + } + + PrimaryKeyBuilder::operator details::SubBuilder() + { + if (m_needsClosing) + { + m_stream << ')'; + m_needsClosing = false; + } + return { m_stream.str() }; + } + + StatementBuilder& StatementBuilder::Select() + { + m_stream << "SELECT "; + m_needsComma = false; + return *this; + } + + StatementBuilder& StatementBuilder::Select(std::string_view column) + { + OutputColumns(m_stream, "SELECT ", column); + return *this; + } + + StatementBuilder& StatementBuilder::Select(std::initializer_list<std::string_view> columns) + { + OutputColumns(m_stream, "SELECT ", columns); + return *this; + } + + StatementBuilder& StatementBuilder::Select(const QualifiedColumn& column) + { + OutputColumns(m_stream, "SELECT ", column); + return *this; + } + + StatementBuilder& StatementBuilder::Select(std::initializer_list<QualifiedColumn> columns) + { + OutputColumns(m_stream, "SELECT ", columns); + return *this; + } + + StatementBuilder& StatementBuilder::Select(details::rowcount_t) + { + m_stream << "SELECT COUNT(*)"; + return *this; + } + + StatementBuilder& StatementBuilder::From() + { + m_stream << " FROM "; + return *this; + } + + StatementBuilder& StatementBuilder::From(std::string_view table) + { + OutputOperationAndTable(m_stream, " FROM", table); + return *this; + } + + StatementBuilder& StatementBuilder::From(QualifiedTable table) + { + OutputOperationAndTable(m_stream, " FROM", table); + return *this; + } + + StatementBuilder& StatementBuilder::From(std::initializer_list<std::string_view> table) + { + OutputOperationAndTable(m_stream, " FROM", table); + return *this; + } + + StatementBuilder& StatementBuilder::Where(std::string_view column) + { + OutputColumns(m_stream, " WHERE ", column); + return *this; + } + + StatementBuilder& StatementBuilder::Where(const QualifiedColumn& column) + { + OutputColumns(m_stream, " WHERE ", column); + return *this; + } + + StatementBuilder& StatementBuilder::WhereValueContainsEmbeddedNullCharacter(std::string_view column) + { + OutputColumns(m_stream, " WHERE instr(", column); + m_stream << ",char(0))>0"; + return *this; + } + + StatementBuilder& StatementBuilder::WhereValueContainsEmbeddedNullCharacter(const QualifiedColumn& column) + { + OutputColumns(m_stream, " WHERE instr(", column); + m_stream << ",char(0))>0"; + return *this; + } + + StatementBuilder& StatementBuilder::Equals(details::unbound_t, std::optional<size_t> index) + { + AppendOpAndBinder(Op::Equals, index); + return *this; + } + + StatementBuilder& StatementBuilder::Equals(std::nullptr_t) + { + // This is almost certainly not what you want. + // In SQL, value = NULL is always false. + // Use StatementBuilder::IsNull instead. + THROW_HR(E_NOTIMPL); + } + + StatementBuilder& StatementBuilder::Equals() + { + m_stream << " ="; + return *this; + } + + StatementBuilder& StatementBuilder::LikeWithEscape(std::string_view value) + { + AddBindFunctor(AppendOpAndBinder(Op::Like), EscapeStringForLike(value)); + return Escape(EscapeCharForLike); + } + + StatementBuilder& StatementBuilder::Like(details::unbound_t) + { + AppendOpAndBinder(Op::Like); + return *this; + } + + StatementBuilder& StatementBuilder::LiteralColumn(std::string_view value) + { + if (m_needsComma) + { + m_stream << ", "; + } + AddBindFunctor(AppendOpAndBinder(Op::Literal), value); + m_needsComma = true; + return *this; + } + + StatementBuilder& StatementBuilder::Escape(std::string_view escapeChar) + { + THROW_HR_IF(E_INVALIDARG, escapeChar.length() != 1); + AddBindFunctor(AppendOpAndBinder(Op::Escape), escapeChar); + return *this; + } + + StatementBuilder& StatementBuilder::Not() + { + m_stream << " NOT"; + return *this; + } + + StatementBuilder& StatementBuilder::In() + { + m_stream << " IN"; + return *this; + } + + StatementBuilder& StatementBuilder::In(size_t count) + { + m_stream << " IN ("; + for (size_t i = 0; i < count; ++i) + { + m_stream << (i == 0 ? "?" : ", ?"); + } + m_stream << ')'; + + m_bindIndex += static_cast<int>(count); + return *this; + } + + StatementBuilder& StatementBuilder::IsNull(bool isNull) + { + m_stream << " IS " << (isNull ? "" : "NOT ") << "NULL"; + return *this; + } + + StatementBuilder& StatementBuilder::And(std::string_view column) + { + OutputColumns(m_stream, " AND ", column); + return *this; + } + + StatementBuilder& StatementBuilder::And(const QualifiedColumn& column) + { + OutputColumns(m_stream, " AND ", column); + return *this; + } + + StatementBuilder& StatementBuilder::Or(const QualifiedColumn& column) + { + OutputColumns(m_stream, " OR ", column); + return *this; + } + + StatementBuilder& StatementBuilder::Join(std::string_view table) + { + OutputOperationAndTable(m_stream, " JOIN", table); + return *this; + } + + StatementBuilder& StatementBuilder::Join(QualifiedTable table) + { + OutputOperationAndTable(m_stream, " JOIN", table); + return *this; + } + + StatementBuilder& StatementBuilder::Join(std::initializer_list<std::string_view> table) + { + OutputOperationAndTable(m_stream, " JOIN", table); + return *this; + } + + StatementBuilder& StatementBuilder::LeftOuterJoin(std::string_view table) + { + OutputOperationAndTable(m_stream, " LEFT OUTER JOIN", table); + return *this; + } + + StatementBuilder& StatementBuilder::LeftOuterJoin(QualifiedTable table) + { + OutputOperationAndTable(m_stream, " LEFT OUTER JOIN", table); + return *this; + } + + StatementBuilder& StatementBuilder::LeftOuterJoin(std::initializer_list<std::string_view> table) + { + OutputOperationAndTable(m_stream, " LEFT OUTER JOIN", table); + return *this; + } + + StatementBuilder& StatementBuilder::On(const QualifiedColumn& column1, const QualifiedColumn& column2) + { + m_stream << " ON " << column1 << " = " << column2; + return *this; + } + + StatementBuilder& StatementBuilder::Limit(size_t rowCount) + { + m_stream << " LIMIT " << rowCount; + return *this; + } + + StatementBuilder& StatementBuilder::GroupBy(std::string_view column) + { + OutputColumns(m_stream, " GROUP BY ", column); + return *this; + } + + StatementBuilder& StatementBuilder::GroupBy(const QualifiedColumn& column) + { + OutputColumns(m_stream, " GROUP BY ", column); + return *this; + } + + StatementBuilder& StatementBuilder::OrderBy(std::string_view column) + { + OutputColumns(m_stream, " ORDER BY ", column); + return *this; + } + + StatementBuilder& StatementBuilder::OrderBy(const QualifiedColumn& column) + { + OutputColumns(m_stream, " ORDER BY ", column); + return *this; + } + + StatementBuilder& StatementBuilder::Ascending() + { + m_stream << " ASC"; + return *this; + } + + StatementBuilder& StatementBuilder::Descending() + { + m_stream << " DESC"; + return *this; + } + + StatementBuilder& StatementBuilder::InsertInto(std::string_view table) + { + OutputOperationAndTable(m_stream, "INSERT INTO", table); + return *this; + } + + StatementBuilder& StatementBuilder::InsertInto(QualifiedTable table) + { + OutputOperationAndTable(m_stream, "INSERT INTO", table); + return *this; + } + + StatementBuilder& StatementBuilder::InsertInto(std::initializer_list<std::string_view> table) + { + OutputOperationAndTable(m_stream, "INSERT INTO", table); + return *this; + } + + StatementBuilder& StatementBuilder::Columns(std::string_view column) + { + OutputColumns(m_stream, "(", column); + m_stream << ')'; + return *this; + } + + StatementBuilder& StatementBuilder::Columns(std::initializer_list<std::string_view> columns) + { + OutputColumns(m_stream, "(", columns); + m_stream << ')'; + return *this; + } + + StatementBuilder& StatementBuilder::Columns(const QualifiedColumn& column) + { + OutputColumns(m_stream, "(", column); + m_stream << ')'; + return *this; + } + + StatementBuilder& StatementBuilder::Columns(std::initializer_list<QualifiedColumn> columns) + { + OutputColumns(m_stream, "(", columns); + m_stream << ')'; + return *this; + } + + StatementBuilder& StatementBuilder::Columns(std::initializer_list<details::SubBuilder> columns) + { + OutputColumns(m_stream, "(", columns); + m_stream << ')'; + return *this; + } + + StatementBuilder& StatementBuilder::BeginColumns() + { + m_stream << '('; + m_needsComma = false; + return *this; + } + + StatementBuilder& StatementBuilder::Column(std::string_view column) + { + if (m_needsComma) + { + m_stream << ", "; + } + OutputColumns(m_stream, "", column); + m_needsComma = true; + return *this; + } + + StatementBuilder& StatementBuilder::Column(const QualifiedColumn& column) + { + if (m_needsComma) + { + m_stream << ", "; + } + OutputColumns(m_stream, "", column); + m_needsComma = true; + return *this; + } + + StatementBuilder& StatementBuilder::Column(Aggregate aggOp, std::string_view column) + { + if (m_needsComma) + { + m_stream << ", "; + } + OutputColumns(m_stream, aggOp, column); + m_needsComma = true; + return *this; + } + + StatementBuilder& StatementBuilder::Column(Aggregate aggOp, const QualifiedColumn& column) + { + if (m_needsComma) + { + m_stream << ", "; + } + OutputColumns(m_stream, aggOp, column); + m_needsComma = true; + return *this; + } + + StatementBuilder& StatementBuilder::Column(const details::SubBuilder& column) + { + if (m_needsComma) + { + m_stream << ", "; + } + m_stream << column; + m_needsComma = true; + return *this; + } + + StatementBuilder& StatementBuilder::EndColumns() + { + m_stream << ')'; + m_needsComma = false; + return *this; + } + + StatementBuilder& StatementBuilder::BeginValues() + { + m_stream << " VALUES ("; + m_needsComma = false; + return *this; + } + + StatementBuilder& StatementBuilder::EndValues() + { + m_stream << ')'; + m_needsComma = false; + return *this; + } + + StatementBuilder& StatementBuilder::CreateTable(std::string_view table) + { + OutputOperationAndTable(m_stream, "CREATE TABLE", table); + return *this; + } + + StatementBuilder& StatementBuilder::CreateTable(QualifiedTable table) + { + OutputOperationAndTable(m_stream, "CREATE TABLE", table); + return *this; + } + + StatementBuilder& StatementBuilder::CreateTable(std::initializer_list<std::string_view> table) + { + OutputOperationAndTable(m_stream, "CREATE TABLE", table); + return *this; + } + + StatementBuilder& StatementBuilder::AlterTable(std::string_view table) + { + OutputOperationAndTable(m_stream, "ALTER TABLE", table); + return *this; + } + + StatementBuilder& StatementBuilder::AlterTable(QualifiedTable table) + { + OutputOperationAndTable(m_stream, "ALTER TABLE", table); + return *this; + } + + StatementBuilder& StatementBuilder::AlterTable(std::initializer_list<std::string_view> table) + { + OutputOperationAndTable(m_stream, "ALTER TABLE", table); + return *this; + } + + StatementBuilder& StatementBuilder::Add(std::string_view column, Type type) + { + m_stream << " ADD " << column; + OutputType(m_stream, type); + return *this; + } + + StatementBuilder& StatementBuilder::DropTable(std::string_view table) + { + OutputOperationAndTable(m_stream, "DROP TABLE", table); + return *this; + } + + StatementBuilder& StatementBuilder::DropTable(QualifiedTable table) + { + OutputOperationAndTable(m_stream, "DROP TABLE", table); + return *this; + } + + StatementBuilder& StatementBuilder::DropTable(std::initializer_list<std::string_view> table) + { + OutputOperationAndTable(m_stream, "DROP TABLE", table); + return *this; + } + + StatementBuilder& StatementBuilder::CreateIndex(std::string_view table) + { + OutputOperationAndTable(m_stream, "CREATE INDEX", table); + return *this; + } + + StatementBuilder& StatementBuilder::CreateIndex(QualifiedTable table) + { + OutputOperationAndTable(m_stream, "CREATE INDEX", table); + return *this; + } + + StatementBuilder& StatementBuilder::CreateIndex(std::initializer_list<std::string_view> table) + { + OutputOperationAndTable(m_stream, "CREATE INDEX", table); + return *this; + } + + StatementBuilder& StatementBuilder::CreateUniqueIndex(std::string_view table) + { + OutputOperationAndTable(m_stream, "CREATE UNIQUE INDEX", table); + return *this; + } + + StatementBuilder& StatementBuilder::CreateUniqueIndex(QualifiedTable table) + { + OutputOperationAndTable(m_stream, "CREATE UNIQUE INDEX", table); + return *this; + } + + StatementBuilder& StatementBuilder::CreateUniqueIndex(std::initializer_list<std::string_view> table) + { + OutputOperationAndTable(m_stream, "CREATE UNIQUE INDEX", table); + return *this; + } + + StatementBuilder& StatementBuilder::DropIndex(std::string_view index) + { + OutputOperationAndTable(m_stream, "DROP INDEX", index); + return *this; + } + + StatementBuilder& StatementBuilder::DropIndex(QualifiedTable index) + { + OutputOperationAndTable(m_stream, "DROP INDEX", index); + return *this; + } + + StatementBuilder& StatementBuilder::DropIndex(std::initializer_list<std::string_view> index) + { + OutputOperationAndTable(m_stream, "DROP INDEX", index); + return *this; + } + + StatementBuilder& StatementBuilder::On(std::string_view table) + { + OutputOperationAndTable(m_stream, " ON", table); + return *this; + } + + StatementBuilder& StatementBuilder::On(std::initializer_list<std::string_view> table) + { + OutputOperationAndTable(m_stream, " ON", table); + return *this; + } + + StatementBuilder& StatementBuilder::DeleteFrom(std::string_view table) + { + OutputOperationAndTable(m_stream, "DELETE FROM", table); + return *this; + } + + StatementBuilder& StatementBuilder::DeleteFrom(QualifiedTable table) + { + OutputOperationAndTable(m_stream, "DELETE FROM", table); + return *this; + } + + StatementBuilder& StatementBuilder::DeleteFrom(std::initializer_list<std::string_view> table) + { + OutputOperationAndTable(m_stream, "DELETE FROM", table); + return *this; + } + + StatementBuilder& StatementBuilder::Update(std::string_view table) + { + OutputOperationAndTable(m_stream, "UPDATE", table); + return *this; + } + + StatementBuilder& StatementBuilder::Update(QualifiedTable table) + { + OutputOperationAndTable(m_stream, "UPDATE", table); + return *this; + } + + StatementBuilder& StatementBuilder::Update(std::initializer_list<std::string_view> table) + { + OutputOperationAndTable(m_stream, "UPDATE", table); + return *this; + } + + StatementBuilder& StatementBuilder::UpdateOrReplace(std::string_view table) + { + OutputOperationAndTable(m_stream, "UPDATE OR REPLACE", table); + return *this; + } + + StatementBuilder& StatementBuilder::UpdateOrReplace(QualifiedTable table) + { + OutputOperationAndTable(m_stream, "UPDATE OR REPLACE", table); + return *this; + } + + StatementBuilder& StatementBuilder::UpdateOrReplace(std::initializer_list<std::string_view> table) + { + OutputOperationAndTable(m_stream, "UPDATE OR REPLACE", table); + return *this; + } + + StatementBuilder& StatementBuilder::Set() + { + m_stream << " SET "; + m_needsComma = false; + return *this; + } + + StatementBuilder& StatementBuilder::Vacuum() + { + m_stream << "VACUUM"; + return *this; + } + + StatementBuilder& StatementBuilder::BeginParenthetical() + { + m_stream << '('; + return *this; + } + + StatementBuilder& StatementBuilder::EndParenthetical() + { + m_stream << ')'; + return *this; + } + + StatementBuilder& StatementBuilder::WithoutRowID() + { + m_stream << " WITHOUT ROWID"; + return *this; + } + + + StatementBuilder& StatementBuilder::As(std::string_view alias) + { + OutputOperationAndTable(m_stream, " AS", alias); + return *this; + } + + Statement StatementBuilder::Prepare(const Connection& connection) + { + Statement result = Statement::Create(connection, m_stream.str()); + for (const auto& f : m_binders) + { + f(result); + } + return result; + } + + void StatementBuilder::Execute(const Connection& connection) + { + Prepare(connection).Execute(); + } + + int StatementBuilder::AppendOpAndBinder(Op op, std::optional<size_t> index) + { + switch (op) + { + case Op::Equals: + m_stream << " = ?"; + break; + case Op::Like: + m_stream << " LIKE ?"; + break; + case Op::Escape: + m_stream << " ESCAPE ?"; + break; + case Op::Literal: + m_stream << " ?"; + break; + default: + THROW_HR(E_UNEXPECTED); + } + + if (index) + { + m_stream << index.value(); + } + + return m_bindIndex++; + } + + int StatementBuilder::AppendValuesAndBinders(size_t count) + { + m_stream << " VALUES ("; + for (size_t i = 0; i < count; ++i) + { + m_stream << (i == 0 ? "?" : ", ?"); + } + m_stream << ')'; + + int result = m_bindIndex; + m_bindIndex += static_cast<int>(count); + return result; + } + + int StatementBuilder::AppendValueAndBinder() + { + if (m_needsComma) + { + m_stream << ", "; + } + m_stream << '?'; + m_needsComma = true; + return m_bindIndex++; + } +} diff --git a/src/AppInstallerSharedLib/SQLiteStorageBase.cpp b/src/AppInstallerSharedLib/SQLiteStorageBase.cpp @@ -0,0 +1,115 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "Public/winget/SQLiteStorageBase.h" +#include "Public/winget/SQLiteMetadataTable.h" +#include "AppInstallerDateTime.h" + +namespace AppInstaller::SQLite +{ + namespace + { + static char const* const GetOpenDispositionString(SQLiteStorageBase::OpenDisposition disposition) + { + switch (disposition) + { + case SQLiteStorageBase::OpenDisposition::Read: + return "Read"; + case SQLiteStorageBase::OpenDisposition::ReadWrite: + return "ReadWrite"; + case SQLiteStorageBase::OpenDisposition::Immutable: + return "ImmutableRead"; + default: + return "Unknown"; + } + } + } + + // One method for converting open disposition to proper open disposition + // another method for obtaining the right flags + void SQLiteStorageBase::SetLastWriteTime() + { + MetadataTable::SetNamedValue(m_dbconn, s_MetadataValueName_LastWriteTime, Utility::GetCurrentUnixEpoch()); + } + + // Recording last write time based on MSDN documentation stating that time returns a POSIX epoch time and thus + // should be consistent across systems. + std::chrono::system_clock::time_point SQLiteStorageBase::GetLastWriteTime() + { + int64_t lastWriteTime = MetadataTable::GetNamedValue<int64_t>(m_dbconn, s_MetadataValueName_LastWriteTime); + return Utility::ConvertUnixEpochToSystemClock(lastWriteTime); + } + + SQLiteStorageBase::SQLiteStorageBase(const std::string& filePath, OpenDisposition disposition, Utility::ManagedFile&& file) : + m_indexFile(std::move(file)) + { + AICLI_LOG(Repo, Info, << "Opening database for " << GetOpenDispositionString(disposition) << " at '" << filePath << "'"); + switch (disposition) + { + case OpenDisposition::Read: + m_dbconn = SQLite::Connection::Create(filePath, SQLite::Connection::OpenDisposition::ReadOnly, SQLite::Connection::OpenFlags::None); + break; + case OpenDisposition::ReadWrite: + m_dbconn = SQLite::Connection::Create(filePath, SQLite::Connection::OpenDisposition::ReadWrite, SQLite::Connection::OpenFlags::None); + break; + case OpenDisposition::Immutable: + { + // Following the algorithm set forth at https://sqlite.org/uri.html [3.1] to convert to a URI path + // The execution order builds out the string so that it shouldn't require any moves (other than growing) + std::string target; + // Add an 'arbitrary' growth size to prevent the majority of needing to grow (adding 'file:/' and '?immutable=1') + target.reserve(filePath.size() + 20); + + target += "file:"; + + bool wasLastCharSlash = false; + + if (filePath.size() >= 2 && filePath[1] == ':' && + ((filePath[0] >= 'a' && filePath[0] <= 'z') || + (filePath[0] >= 'A' && filePath[0] <= 'Z'))) + { + target += '/'; + wasLastCharSlash = true; + } + + for (char c : filePath) + { + bool wasThisCharSlash = false; + switch (c) + { + case '?': target += "%3f"; break; + case '#': target += "%23"; break; + case '\\': + case '/': + { + wasThisCharSlash = true; + if (!wasLastCharSlash) + { + target += '/'; + } + break; + } + default: target += c; break; + } + + wasLastCharSlash = wasThisCharSlash; + } + + target += "?immutable=1"; + m_dbconn = SQLite::Connection::Create(filePath, SQLite::Connection::OpenDisposition::ReadOnly, SQLite::Connection::OpenFlags::Uri); + break; + } + default: + THROW_HR(E_UNEXPECTED); + } + + m_version = Version::GetSchemaVersion(m_dbconn); + } + + SQLiteStorageBase::SQLiteStorageBase(const std::string& target, const Version& version) : + m_dbconn(SQLite::Connection::Create(target, SQLite::Connection::OpenDisposition::Create)) + { + m_version = version; + MetadataTable::Create(m_dbconn); + } +}+ \ No newline at end of file diff --git a/src/AppInstallerSharedLib/SQLiteTempTable.cpp b/src/AppInstallerSharedLib/SQLiteTempTable.cpp @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "Public/winget/SQLiteTempTable.h" +#include "AppInstallerStrings.h" + + +namespace AppInstaller::SQLite +{ + using namespace std::string_view_literals; + + TempTable::TempTable() + { + GUID tempName; + THROW_IF_FAILED(CoCreateGuid(&tempName)); + + wchar_t guidAsString[MAX_PATH]; + THROW_HR_IF(E_UNEXPECTED, StringFromGUID2(tempName, guidAsString, MAX_PATH) == 0); + + m_name = Utility::ConvertToUTF8(guidAsString); + } + + TempTable::~TempTable() + { + if (m_dropTableStatement) + { + m_dropTableStatement.Execute(); + } + } + + Builder::QualifiedTable TempTable::GetQualifiedName() const + { + return Builder::QualifiedTable("temp"sv, m_name); + } + + void TempTable::InitDropStatement(const Connection& connection) + { + Builder::StatementBuilder builder; + builder.DropTable(m_name); + + m_dropTableStatement = builder.Prepare(connection); + } +} diff --git a/src/AppInstallerSharedLib/SQLiteVersion.cpp b/src/AppInstallerSharedLib/SQLiteVersion.cpp @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "Public/winget/SQLiteVersion.h" +#include "Public/winget/SQLiteMetadataTable.h" + +#include <limits> + +namespace AppInstaller::SQLite +{ + Version Version::GetSchemaVersion(Connection& connection) + { + int major = MetadataTable::GetNamedValue<int>(connection, s_MetadataValueName_MajorVersion); + int minor = MetadataTable::GetNamedValue<int>(connection, s_MetadataValueName_MinorVersion); + + return { static_cast<uint32_t>(major), static_cast<uint32_t>(minor) }; + } + + void Version::SetSchemaVersion(Connection& connection) + { + Savepoint savepoint = Savepoint::Create(connection, "version_setschemaversion"); + + MetadataTable::SetNamedValue(connection, s_MetadataValueName_MajorVersion, static_cast<int>(MajorVersion)); + MetadataTable::SetNamedValue(connection, s_MetadataValueName_MinorVersion, static_cast<int>(MinorVersion)); + + savepoint.Commit(); + } + + std::ostream& operator<<(std::ostream& out, const Version& version) + { + if (version.IsLatest()) + { + return out << "Latest"; + } + else if (version.IsLatestForMajor(version.MajorVersion)) + { + return out << version.MajorVersion << ".Latest"; + } + else + { + return out << version.MajorVersion << '.' << version.MinorVersion; + } + } + + Version Version::Latest() + { + return { std::numeric_limits<uint32_t>::max(), std::numeric_limits<uint32_t>::max() }; + } + + Version Version::LatestForMajor(uint32_t majorVersion) + { + return { majorVersion, std::numeric_limits<uint32_t>::max() }; + } + + bool Version::IsLatest() const + { + return (MajorVersion == std::numeric_limits<uint32_t>::max() && MinorVersion == std::numeric_limits<uint32_t>::max()); + } + + bool Version::IsLatestForMajor(uint32_t majorVersion) const + { + return (MajorVersion == majorVersion && MinorVersion == std::numeric_limits<uint32_t>::max()); + } +} diff --git a/src/AppInstallerSharedLib/SQLiteWrapper.cpp b/src/AppInstallerSharedLib/SQLiteWrapper.cpp @@ -0,0 +1,375 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "Public/winget/SQLiteWrapper.h" +#include "Public/AppInstallerErrors.h" +#include "ICU/SQLiteICU.h" + +#include <wil/result_macros.h> + +using namespace std::string_view_literals; + +// Enable this to have all Statement constructions output the associated query plan. +#define WINGET_SQLITE_EXPLAIN_QUERY_PLAN_ENABLED 0 + +#if WINGET_SQLITE_EXPLAIN_QUERY_PLAN_ENABLED +#include <stack> +#endif + +#define THROW_SQLITE(_error_,_connection_) \ + do { \ + int _ts_sqliteReturnValue = _error_; \ + sqlite3* _ts_sqliteConnection = _connection_; \ + THROW_EXCEPTION_MSG(SQLiteException(_ts_sqliteReturnValue), _ts_sqliteConnection ? sqlite3_errmsg(_ts_sqliteConnection) : sqlite3_errstr(_ts_sqliteReturnValue)); \ + } while (0,0) + +#define THROW_IF_SQLITE_FAILED(_statement_,_connection_) \ + do { \ + int _tisf_sqliteReturnValue = _statement_; \ + if (_tisf_sqliteReturnValue != SQLITE_OK) \ + { \ + THROW_SQLITE(_tisf_sqliteReturnValue,_connection_); \ + } \ + } while (0,0) + +namespace AppInstaller::SQLite +{ + std::string_view RowIDName = "rowid"sv; + + namespace + { + size_t GetNextConnectionId() + { + static std::atomic_size_t connectionId(0); + return ++connectionId; + } + + size_t GetNextStatementId() + { + static std::atomic_size_t statementId(0); + return ++statementId; + } + } + + namespace details + { + void ParameterSpecificsImpl<nullptr_t>::Bind(sqlite3_stmt* stmt, int index, nullptr_t) + { + THROW_IF_SQLITE_FAILED(sqlite3_bind_null(stmt, index), sqlite3_db_handle(stmt)); + } + + void ThrowIfContainsEmbeddedNullCharacter(std::string_view v) + { + THROW_HR_IF(APPINSTALLER_CLI_ERROR_BIND_WITH_EMBEDDED_NULL, v.find('\0') != std::string_view::npos); + } + + void ParameterSpecificsImpl<std::string>::Bind(sqlite3_stmt* stmt, int index, const std::string& v) + { + ThrowIfContainsEmbeddedNullCharacter(v); + THROW_IF_SQLITE_FAILED(sqlite3_bind_text64(stmt, index, v.c_str(), v.size(), SQLITE_TRANSIENT, SQLITE_UTF8), sqlite3_db_handle(stmt)); + } + + std::string ParameterSpecificsImpl<std::string>::GetColumn(sqlite3_stmt* stmt, int column) + { + return reinterpret_cast<const char*>(sqlite3_column_text(stmt, column)); + } + + void ParameterSpecificsImpl<std::string_view>::Bind(sqlite3_stmt* stmt, int index, std::string_view v) + { + if (v.empty()) + { + // An empty string_view can have it's data member return nullptr, which effectively binds a null value. + // We don't want that, so instead bind an empty string, which will have a non-null data pointer. + ParameterSpecificsImpl<std::string>::Bind(stmt, index, {}); + } + else + { + ThrowIfContainsEmbeddedNullCharacter(v); + THROW_IF_SQLITE_FAILED(sqlite3_bind_text64(stmt, index, v.data(), v.size(), SQLITE_TRANSIENT, SQLITE_UTF8), sqlite3_db_handle(stmt)); + } + } + + void ParameterSpecificsImpl<int>::Bind(sqlite3_stmt* stmt, int index, int v) + { + THROW_IF_SQLITE_FAILED(sqlite3_bind_int(stmt, index, v), sqlite3_db_handle(stmt)); + } + + int ParameterSpecificsImpl<int>::GetColumn(sqlite3_stmt* stmt, int column) + { + return sqlite3_column_int(stmt, column); + } + + void ParameterSpecificsImpl<int64_t>::Bind(sqlite3_stmt* stmt, int index, int64_t v) + { + THROW_IF_SQLITE_FAILED(sqlite3_bind_int64(stmt, index, v), sqlite3_db_handle(stmt)); + } + + int64_t ParameterSpecificsImpl<int64_t>::GetColumn(sqlite3_stmt* stmt, int column) + { + return sqlite3_column_int64(stmt, column); + } + + void ParameterSpecificsImpl<bool>::Bind(sqlite3_stmt* stmt, int index, bool v) + { + THROW_IF_SQLITE_FAILED(sqlite3_bind_int(stmt, index, (v ? 1 : 0)), sqlite3_db_handle(stmt)); + } + + bool ParameterSpecificsImpl<bool>::GetColumn(sqlite3_stmt* stmt, int column) + { + return (sqlite3_column_int(stmt, column) != 0); + } + + std::string ParameterSpecificsImpl<blob_t>::ToLog(const blob_t& v) + { + std::ostringstream strstr; + strstr << "blob[" << v.size() << "]"; + return strstr.str(); + } + + void ParameterSpecificsImpl<blob_t>::Bind(sqlite3_stmt* stmt, int index, const blob_t& v) + { + THROW_IF_SQLITE_FAILED(sqlite3_bind_blob64(stmt, index, v.data(), v.size(), SQLITE_TRANSIENT), sqlite3_db_handle(stmt)); + } + + blob_t ParameterSpecificsImpl<blob_t>::GetColumn(sqlite3_stmt* stmt, int column) + { + const blob_t::value_type* blobPtr = reinterpret_cast<const blob_t::value_type *>(sqlite3_column_blob(stmt, column)); + if (blobPtr) + { + int blobBytes = sqlite3_column_bytes(stmt, column); + return blob_t{ blobPtr, blobPtr + blobBytes }; + } + else + { + return {}; + } + } + } + + Connection::Connection(const std::string& target, OpenDisposition disposition, OpenFlags flags) + { + m_id = GetNextConnectionId(); + AICLI_LOG(SQL, Info, << "Opening SQLite connection #" << m_id << ": '" << target << "' [" << std::hex << static_cast<int>(disposition) << ", " << std::hex << static_cast<int>(flags) << "]"); + // Always force connection serialization until we determine that there are situations where it is not needed + int resultingFlags = static_cast<int>(disposition) | static_cast<int>(flags) | SQLITE_OPEN_FULLMUTEX; + THROW_IF_SQLITE_FAILED(sqlite3_open_v2(target.c_str(), &m_dbconn, resultingFlags, nullptr), nullptr); + } + + Connection Connection::Create(const std::string& target, OpenDisposition disposition, OpenFlags flags) + { + Connection result{ target, disposition, flags }; + + THROW_IF_SQLITE_FAILED(sqlite3_extended_result_codes(result.m_dbconn.get(), 1), result.m_dbconn.get()); + + return result; + } + + void Connection::EnableICU() + { + AICLI_LOG(SQL, Verbose, << "Enabling ICU"); + THROW_IF_SQLITE_FAILED(sqlite3IcuInit(m_dbconn.get()), m_dbconn.get()); + } + + rowid_t Connection::GetLastInsertRowID() + { + return sqlite3_last_insert_rowid(m_dbconn.get()); + } + + int Connection::GetChanges() const + { + return sqlite3_changes(m_dbconn.get()); + } + + size_t Connection::GetID() const + { + return m_id; + } + + Statement::Statement(const Connection& connection, std::string_view sql) + { + m_connectionId = connection.GetID(); + m_id = GetNextStatementId(); + AICLI_LOG(SQL, Verbose, << "Preparing statement #" << m_connectionId << '-' << m_id << ": " << sql); + // SQL string size should include the null terminator (https://www.sqlite.org/c3ref/prepare.html) + assert(sql.data()[sql.size()] == '\0'); + THROW_IF_SQLITE_FAILED(sqlite3_prepare_v2(connection, sql.data(), static_cast<int>(sql.size() + 1), &m_stmt, nullptr), connection); + } + +#if WINGET_SQLITE_EXPLAIN_QUERY_PLAN_ENABLED +#define WINGET_SQLITE_EXPLAIN_QUERY_PLAN(_connection_,_sql_) \ + std::string _explainStatementSQL_ = "EXPLAIN QUERY PLAN "; \ + _explainStatementSQL_.append(_sql_); \ + try { \ + Statement _explainStatement_(_connection_,_explainStatementSQL_); \ + LogExplainQueryPlanResult(_sql_, _explainStatement_); \ + } catch(...) {} + + void LogExplainQueryPlanResult(std::string_view sql, Statement& plan) + { + bool outputHeader = true; + std::stack<int> parents; + + while (plan.Step()) + { + if (outputHeader) + { + AICLI_LOG(SQL, Info, << "Query plan for: " << sql); + outputHeader = false; + } + + int id = plan.GetColumn<int>(0); + int parent = plan.GetColumn<int>(1); + + while (!parents.empty() && parents.top() != parent) + { + parents.pop(); + } + + AICLI_LOG(SQL, Info, << "|-" << std::string(parents.size() * 2, '-') << ' ' << plan.GetColumn<std::string>(3)); + + parents.push(id); + } + } +#else +#define WINGET_SQLITE_EXPLAIN_QUERY_PLAN(_connection_,_sql_) +#endif + + Statement Statement::Create(const Connection& connection, const std::string& sql) + { + WINGET_SQLITE_EXPLAIN_QUERY_PLAN(connection, sql); + return { connection, { sql.c_str(), sql.size() } }; + } + + Statement Statement::Create(const Connection& connection, std::string_view sql) + { + WINGET_SQLITE_EXPLAIN_QUERY_PLAN(connection, sql); + // We need the statement to be null terminated, and the only way to guarantee that with a string_view is to construct a string copy. + return Create(connection, std::string(sql)); + } + + Statement Statement::Create(const Connection& connection, char const* const sql) + { + WINGET_SQLITE_EXPLAIN_QUERY_PLAN(connection, sql); + return { connection, sql }; + } + + bool Statement::Step(bool failFastOnError) + { + AICLI_LOG(SQL, Verbose, << "Stepping statement #" << m_connectionId << '-' << m_id); + int result = sqlite3_step(m_stmt.get()); + + if (result == SQLITE_ROW) + { + AICLI_LOG(SQL, Verbose, << "Statement #" << m_connectionId << '-' << m_id << " has data"); + m_state = State::HasRow; + return true; + } + else if (result == SQLITE_DONE) + { + AICLI_LOG(SQL, Verbose, << "Statement #" << m_connectionId << '-' << m_id << " has completed"); + m_state = State::Completed; + return false; + } + else + { + m_state = State::Error; + if (failFastOnError) + { + FAIL_FAST_MSG("Critical SQL statement failed"); + } + else + { + THROW_SQLITE(result, sqlite3_db_handle(m_stmt.get())); + } + } + } + + void Statement::Execute(bool failFastOnError) + { + THROW_HR_IF(E_UNEXPECTED, Step(failFastOnError)); + } + + bool Statement::GetColumnIsNull(int column) + { + int type = sqlite3_column_type(m_stmt.get(), column); + return type == SQLITE_NULL; + } + + void Statement::Reset() + { + AICLI_LOG(SQL, Verbose, << "Reset statement #" << m_connectionId << '-' << m_id); + // Ignore return value from reset, as if it is an error, it was the error from the last call to step. + sqlite3_reset(m_stmt.get()); + m_state = State::Prepared; + } + + Savepoint::Savepoint(Connection& connection, std::string&& name) : + m_name(std::move(name)) + { + using namespace std::string_literals; + + Statement begin = Statement::Create(connection, "SAVEPOINT ["s + m_name + "]"); + m_rollbackTo = Statement::Create(connection, "ROLLBACK TO ["s + m_name + "]"); + m_release = Statement::Create(connection, "RELEASE ["s + m_name + "]"); + + AICLI_LOG(SQL, Verbose, << "Begin savepoint: " << m_name); + begin.Step(); + } + + Savepoint Savepoint::Create(Connection& connection, std::string name) + { + return { connection, std::move(name) }; + } + + Savepoint::~Savepoint() + { + Rollback(); + } + + void Savepoint::Rollback() + { + if (m_inProgress) + { + AICLI_LOG(SQL, Verbose, << "Roll back savepoint: " << m_name); + m_rollbackTo.Step(true); + // 'ROLLBACK TO' *DOES NOT* remove the savepoint from the transaction stack. + // In order to remove it, we must RELEASE. Since we just invoked a ROLLBACK TO + // this should have the effect of 'committing' nothing. + m_release.Step(true); + m_inProgress = false; + } + } + + void Savepoint::Commit() + { + if (m_inProgress) + { + AICLI_LOG(SQL, Verbose, << "Commit savepoint: " << m_name); + m_release.Step(true); + m_inProgress = false; + } + } + + std::string_view EscapeCharForLike = "'"sv; + + std::string EscapeStringForLike(std::string_view value) + { + constexpr char singleChar = '_'; + constexpr char multiChar = '%'; + char escapeChar = EscapeCharForLike[0]; + + std::string result; + result.reserve(value.length()); + + for (char c : value) + { + if (c == singleChar || c == multiChar || c == escapeChar) + { + result.append(1, escapeChar); + } + result.append(1, c); + } + + return result; + } +} diff --git a/src/WinGetUtil/Exports.cpp b/src/WinGetUtil/Exports.cpp @@ -85,7 +85,7 @@ extern "C" THROW_HR_IF(E_INVALIDARG, !!*index); std::string filePathUtf8 = ConvertToUTF8(filePath); - Schema::Version internalVersion{ majorVersion, minorVersion }; + AppInstaller::SQLite::Version internalVersion{ majorVersion, minorVersion }; std::unique_ptr<SQLiteIndex> result = std::make_unique<SQLiteIndex>(SQLiteIndex::CreateNew(filePathUtf8, internalVersion));