winget-cli

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

commit 9d57d6381fa8fc182b2f446528e51f670fce4bbc
parent 1e35cefb75618ab9ee3b7fe02aa9d66ba3d8345a
Author: JohnMcPMS <johnmcp@microsoft.com>
Date:   Thu,  9 Jan 2020 16:33:03 -0800

Add tables to SQLite index V1.0 (#18)


Diffstat:
Msrc/AppInstallerCLITests/TestCommon.cpp | 9+++++----
Msrc/AppInstallerCLITests/main.cpp | 4++++
Msrc/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj | 17+++++++++++++++++
Msrc/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/AppInstallerRepositoryCore/Manifest/Manifest.h | 4++--
Msrc/AppInstallerRepositoryCore/Microsoft/SQLiteIndex.cpp | 2++
Msrc/AppInstallerRepositoryCore/Microsoft/SQLiteIndex.h | 12++++++++++++
Asrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ChannelTable.h | 22++++++++++++++++++++++
Asrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/CommandsTable.h | 22++++++++++++++++++++++
Asrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ExtensionsTable.h | 22++++++++++++++++++++++
Asrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/IdTable.h | 22++++++++++++++++++++++
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/Interface.cpp | 37+++++++++++++++++++++++++++++++++++++
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/Interface.h | 2++
Asrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ManifestTable.cpp | 30++++++++++++++++++++++++++++++
Asrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ManifestTable.h | 17+++++++++++++++++
Asrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/MonikerTable.h | 22++++++++++++++++++++++
Asrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/NameTable.h | 22++++++++++++++++++++++
Asrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToManyTable.cpp | 42++++++++++++++++++++++++++++++++++++++++++
Asrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToManyTable.h | 25+++++++++++++++++++++++++
Asrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToOneTable.cpp | 22++++++++++++++++++++++
Asrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToOneTable.h | 30++++++++++++++++++++++++++++++
Asrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/PathPartTable.cpp | 30++++++++++++++++++++++++++++++
Asrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/PathPartTable.h | 19+++++++++++++++++++
Asrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ProtocolsTable.h | 22++++++++++++++++++++++
Asrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/TagsTable.h | 22++++++++++++++++++++++
Asrc/AppInstallerRepositoryCore/Microsoft/Schema/1_0/VersionTable.h | 22++++++++++++++++++++++
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/ISQLiteIndex.h | 3+++
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/MetadataTable.cpp | 25+++++++++++--------------
Msrc/AppInstallerRepositoryCore/Microsoft/Schema/MetadataTable.h | 16+++++++++-------
Msrc/AppInstallerRepositoryCore/SQLiteWrapper.cpp | 44+++++++++++++++++++++++++++-----------------
Msrc/AppInstallerRepositoryCore/SQLiteWrapper.h | 24++++++++++++++++--------
Msrc/AppInstallerRepositoryCore/pch.h | 4+++-
32 files changed, 614 insertions(+), 53 deletions(-)

diff --git a/src/AppInstallerCLITests/TestCommon.cpp b/src/AppInstallerCLITests/TestCommon.cpp @@ -7,15 +7,16 @@ namespace TestCommon { namespace { - struct initRand + int initRand() { - initRand() { srand(static_cast<unsigned int>(time(NULL))); } + srand(static_cast<unsigned int>(time(NULL))); + return rand(); }; inline int getRand() { - static initRand srandHolder; - return rand(); + static int randStart = initRand(); + return randStart++; } inline std::string GetTempFilePath(const std::string& baseName, const std::string& baseExt) diff --git a/src/AppInstallerCLITests/main.cpp b/src/AppInstallerCLITests/main.cpp @@ -26,6 +26,10 @@ int main(int argc, char** argv) { TestCommon::TempFile::SetDestructorBehavior(true); } + else if ("-log"s == argv[i]) + { + AppInstaller::Logging::AddDefaultFileLogger(); + } else { args.push_back(argv[i]); diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj @@ -168,7 +168,20 @@ <ClInclude Include="Manifest\Manifest.h" /> <ClInclude Include="Manifest\ManifestInstaller.h" /> <ClInclude Include="Manifest\ManifestLocalization.h" /> + <ClInclude Include="Microsoft\Schema\1_0\ChannelTable.h" /> + <ClInclude Include="Microsoft\Schema\1_0\CommandsTable.h" /> + <ClInclude Include="Microsoft\Schema\1_0\ExtensionsTable.h" /> + <ClInclude Include="Microsoft\Schema\1_0\IdTable.h" /> <ClInclude Include="Microsoft\Schema\1_0\Interface.h" /> + <ClInclude Include="Microsoft\Schema\1_0\ManifestTable.h" /> + <ClInclude Include="Microsoft\Schema\1_0\MonikerTable.h" /> + <ClInclude Include="Microsoft\Schema\1_0\NameTable.h" /> + <ClInclude Include="Microsoft\Schema\1_0\OneToManyTable.h" /> + <ClInclude Include="Microsoft\Schema\1_0\OneToOneTable.h" /> + <ClInclude Include="Microsoft\Schema\1_0\PathPartTable.h" /> + <ClInclude Include="Microsoft\Schema\1_0\ProtocolsTable.h" /> + <ClInclude Include="Microsoft\Schema\1_0\TagsTable.h" /> + <ClInclude Include="Microsoft\Schema\1_0\VersionTable.h" /> <ClInclude Include="Microsoft\Schema\ISQLiteIndex.h" /> <ClInclude Include="Microsoft\Schema\MetadataTable.h" /> <ClInclude Include="Microsoft\Schema\Version.h" /> @@ -182,6 +195,10 @@ <ClCompile Include="Manifest\ManifestInstaller.cpp" /> <ClCompile Include="Manifest\ManifestLocalization.cpp" /> <ClCompile Include="Microsoft\Schema\1_0\Interface.cpp" /> + <ClCompile Include="Microsoft\Schema\1_0\ManifestTable.cpp" /> + <ClCompile Include="Microsoft\Schema\1_0\OneToManyTable.cpp" /> + <ClCompile Include="Microsoft\Schema\1_0\OneToOneTable.cpp" /> + <ClCompile Include="Microsoft\Schema\1_0\PathPartTable.cpp" /> <ClCompile Include="Microsoft\Schema\MetadataTable.cpp" /> <ClCompile Include="Microsoft\Schema\Version.cpp" /> <ClCompile Include="Microsoft\SQLiteIndex.cpp" /> diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters @@ -60,6 +60,45 @@ <ClInclude Include="Manifest\ManifestLocalization.h"> <Filter>Manifest</Filter> </ClInclude> + <ClInclude Include="Microsoft\Schema\1_0\OneToOneTable.h"> + <Filter>Microsoft\Schema\1_0</Filter> + </ClInclude> + <ClInclude Include="Microsoft\Schema\1_0\IdTable.h"> + <Filter>Microsoft\Schema\1_0</Filter> + </ClInclude> + <ClInclude Include="Microsoft\Schema\1_0\ChannelTable.h"> + <Filter>Microsoft\Schema\1_0</Filter> + </ClInclude> + <ClInclude Include="Microsoft\Schema\1_0\MonikerTable.h"> + <Filter>Microsoft\Schema\1_0</Filter> + </ClInclude> + <ClInclude Include="Microsoft\Schema\1_0\NameTable.h"> + <Filter>Microsoft\Schema\1_0</Filter> + </ClInclude> + <ClInclude Include="Microsoft\Schema\1_0\VersionTable.h"> + <Filter>Microsoft\Schema\1_0</Filter> + </ClInclude> + <ClInclude Include="Microsoft\Schema\1_0\OneToManyTable.h"> + <Filter>Microsoft\Schema\1_0</Filter> + </ClInclude> + <ClInclude Include="Microsoft\Schema\1_0\TagsTable.h"> + <Filter>Microsoft\Schema\1_0</Filter> + </ClInclude> + <ClInclude Include="Microsoft\Schema\1_0\CommandsTable.h"> + <Filter>Microsoft\Schema\1_0</Filter> + </ClInclude> + <ClInclude Include="Microsoft\Schema\1_0\ExtensionsTable.h"> + <Filter>Microsoft\Schema\1_0</Filter> + </ClInclude> + <ClInclude Include="Microsoft\Schema\1_0\ProtocolsTable.h"> + <Filter>Microsoft\Schema\1_0</Filter> + </ClInclude> + <ClInclude Include="Microsoft\Schema\1_0\ManifestTable.h"> + <Filter>Microsoft\Schema\1_0</Filter> + </ClInclude> + <ClInclude Include="Microsoft\Schema\1_0\PathPartTable.h"> + <Filter>Microsoft\Schema\1_0</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ClCompile Include="pch.cpp"> @@ -92,6 +131,18 @@ <ClCompile Include="Manifest\ManifestLocalization.cpp"> <Filter>Manifest</Filter> </ClCompile> + <ClCompile Include="Microsoft\Schema\1_0\OneToOneTable.cpp"> + <Filter>Microsoft\Schema\1_0</Filter> + </ClCompile> + <ClCompile Include="Microsoft\Schema\1_0\OneToManyTable.cpp"> + <Filter>Microsoft\Schema\1_0</Filter> + </ClCompile> + <ClCompile Include="Microsoft\Schema\1_0\ManifestTable.cpp"> + <Filter>Microsoft\Schema\1_0</Filter> + </ClCompile> + <ClCompile Include="Microsoft\Schema\1_0\PathPartTable.cpp"> + <Filter>Microsoft\Schema\1_0</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <None Include="PropertySheet.props" /> diff --git a/src/AppInstallerRepositoryCore/Manifest/Manifest.h b/src/AppInstallerRepositoryCore/Manifest/Manifest.h @@ -10,9 +10,9 @@ namespace AppInstaller::Manifest { - class Manifest + // Our representation of the parsed manifest file. + struct Manifest { - public: // Required std::string Id; diff --git a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndex.cpp b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndex.cpp @@ -36,6 +36,8 @@ namespace AppInstaller::Repository::Microsoft // Use calculated version, as incoming version could be 'latest' result.m_version.SetSchemaVersion(result.m_dbconn); + result.m_interface->CreateTables(result.m_dbconn); + savepoint.Commit(); return result; diff --git a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndex.h b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndex.h @@ -3,7 +3,9 @@ #pragma once #include "SQLiteWrapper.h" #include "Microsoft/Schema/Version.h" +#include "Manifest/Manifest.h" +#include <filesystem> #include <limits> #include <memory> #include <string> @@ -39,6 +41,16 @@ namespace AppInstaller::Repository::Microsoft // Gets the schema version of the index. Schema::Version GetVersion() const { return m_version; } + // Adds the manifest at the repository relative path to the index. + void AddManifest(const Manifest::Manifest& manifest, const std::filesystem::path& relativePath); + + // Updates the manifest at the repository relative path in the index. + // If the old manifest does not exist in the index, this is equivalent to AddManifest(newManifest, newRelativePath). + void UpdateManifest(const Manifest::Manifest& oldManifest, const std::filesystem::path& oldRelativePath, const Manifest::Manifest& newManifest, const std::filesystem::path& newRelativePath); + + // Removes the manifest at the repository relative path from the index. + void RemoveManifest(const Manifest::Manifest& manifest, const std::filesystem::path& relativePath); + private: // Constructor used to open an existing index. SQLiteIndex(const std::string& target, SQLite::Connection::OpenDisposition disposition, SQLite::Connection::OpenFlags flags); diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ChannelTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ChannelTable.h @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "Microsoft/Schema/1_0/OneToOneTable.h" + + +namespace AppInstaller::Repository::Microsoft::Schema::V1_0 +{ + namespace details + { + using namespace std::string_view_literals; + + struct ChannelTableInfo + { + inline static std::string_view TableName() { return "channels"sv; } + inline static std::string_view ValueName() { return "channel"sv; } + }; + } + + // The table for Channel. + using ChannelTable = OneToOneTable<details::ChannelTableInfo>; +} diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/CommandsTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/CommandsTable.h @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "Microsoft/Schema/1_0/OneToManyTable.h" + + +namespace AppInstaller::Repository::Microsoft::Schema::V1_0 +{ + namespace details + { + using namespace std::string_view_literals; + + struct CommandsTableInfo + { + inline static std::string_view TableName() { return "commands"sv; } + inline static std::string_view ValueName() { return "command"sv; } + }; + } + + // The table for Commands. + using CommandsTable = OneToManyTable<details::CommandsTableInfo>; +} diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ExtensionsTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ExtensionsTable.h @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "Microsoft/Schema/1_0/OneToManyTable.h" + + +namespace AppInstaller::Repository::Microsoft::Schema::V1_0 +{ + namespace details + { + using namespace std::string_view_literals; + + struct ExtensionsTableInfo + { + inline static std::string_view TableName() { return "extensions"sv; } + inline static std::string_view ValueName() { return "extension"sv; } + }; + } + + // The table for Extensions. + using ExtensionsTable = OneToManyTable<details::ExtensionsTableInfo>; +} diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/IdTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/IdTable.h @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "Microsoft/Schema/1_0/OneToOneTable.h" + + +namespace AppInstaller::Repository::Microsoft::Schema::V1_0 +{ + namespace details + { + using namespace std::string_view_literals; + + struct IdTableInfo + { + inline static std::string_view TableName() { return "ids"sv; } + inline static std::string_view ValueName() { return "id"sv; } + }; + } + + // The table for Id. + using IdTable = OneToOneTable<details::IdTableInfo>; +} diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/Interface.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/Interface.cpp @@ -3,6 +3,20 @@ #include "pch.h" #include "Microsoft/Schema/1_0/Interface.h" +#include "Microsoft/Schema/1_0/IdTable.h" +#include "Microsoft/Schema/1_0/NameTable.h" +#include "Microsoft/Schema/1_0/MonikerTable.h" +#include "Microsoft/Schema/1_0/VersionTable.h" +#include "Microsoft/Schema/1_0/ChannelTable.h" + +#include "Microsoft/Schema/1_0/PathPartTable.h" + +#include "Microsoft/Schema/1_0/ManifestTable.h" + +#include "Microsoft/Schema/1_0/TagsTable.h" +#include "Microsoft/Schema/1_0/CommandsTable.h" +#include "Microsoft/Schema/1_0/ProtocolsTable.h" +#include "Microsoft/Schema/1_0/ExtensionsTable.h" namespace AppInstaller::Repository::Microsoft::Schema::V1_0 { @@ -10,4 +24,27 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 { return { 1, 0 }; } + + void Interface::CreateTables(SQLite::Connection& connection) + { + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "V1_0_createtables"); + + IdTable::Create(connection); + NameTable::Create(connection); + MonikerTable::Create(connection); + VersionTable::Create(connection); + ChannelTable::Create(connection); + + PathPartTable::Create(connection); + + ManifestTable::Create(connection, + { IdTable::ValueName(), NameTable::ValueName(), MonikerTable::ValueName(), VersionTable::ValueName(), ChannelTable::ValueName(), PathPartTable::ValueName() }); + + TagsTable::Create(connection); + CommandsTable::Create(connection); + ProtocolsTable::Create(connection); + ExtensionsTable::Create(connection); + + savepoint.Commit(); + } } diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/Interface.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/Interface.h @@ -9,6 +9,8 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_0 // Interface to this schema version exposed through ISQLiteIndex. struct Interface : public SQLiteIndexBase { + // Version 1.0 Schema::Version GetVersion() const override; + void CreateTables(SQLite::Connection& connection) override; }; } diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ManifestTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ManifestTable.cpp @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "ManifestTable.h" + + +namespace AppInstaller::Repository::Microsoft::Schema::V1_0 +{ + using namespace std::string_view_literals; + static constexpr std::string_view s_ManifestTable_Table_Name = "manifest"sv; + + void ManifestTable::Create(SQLite::Connection& connection, std::initializer_list<std::string_view> values) + { + std::ostringstream createTableSQL; + createTableSQL << "CREATE TABLE [" << s_ManifestTable_Table_Name << "]("; + + bool isFirst = true; + for (const std::string_view& value : values) + { + createTableSQL << (isFirst ? "[" : ",[") << value << "] INT64"; + isFirst = false; + } + + createTableSQL << ')'; + + SQLite::Statement createStatement = SQLite::Statement::Create(connection, createTableSQL.str()); + + createStatement.Execute(); + } +} diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ManifestTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ManifestTable.h @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "SQLiteWrapper.h" +#include <initializer_list> +#include <string_view> + + +namespace AppInstaller::Repository::Microsoft::Schema::V1_0 +{ + // A table that represents a single manifest + struct ManifestTable + { + // Creates the table. + static void Create(SQLite::Connection& connection, std::initializer_list<std::string_view> values); + }; +} diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/MonikerTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/MonikerTable.h @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "Microsoft/Schema/1_0/OneToOneTable.h" + + +namespace AppInstaller::Repository::Microsoft::Schema::V1_0 +{ + namespace details + { + using namespace std::string_view_literals; + + struct MonikerTableInfo + { + inline static std::string_view TableName() { return "monikers"sv; } + inline static std::string_view ValueName() { return "moniker"sv; } + }; + } + + // The table for Moniker. + using MonikerTable = OneToOneTable<details::MonikerTableInfo>; +} diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/NameTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/NameTable.h @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "Microsoft/Schema/1_0/OneToOneTable.h" + + +namespace AppInstaller::Repository::Microsoft::Schema::V1_0 +{ + namespace details + { + using namespace std::string_view_literals; + + struct NameTableInfo + { + inline static std::string_view TableName() { return "names"sv; } + inline static std::string_view ValueName() { return "name"sv; } + }; + } + + // The table for Name. + using NameTable = OneToOneTable<details::NameTableInfo>; +} diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToManyTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToManyTable.cpp @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "Microsoft/Schema/1_0/OneToManyTable.h" + + +namespace AppInstaller::Repository::Microsoft::Schema::V1_0 +{ + namespace details + { + using namespace std::string_view_literals; + static constexpr std::string_view s_OneToManyTable_MapTable_ManifestName = "manifest"sv; + static constexpr std::string_view s_OneToManyTable_MapTable_Suffix = "_map"sv; + + void CreateOneToManyTable(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName) + { + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, std::string{ tableName } +"_create"); + + // Create the table itself + std::ostringstream createTableSQL; + createTableSQL << "CREATE TABLE [" << tableName << "](" + << '[' << valueName << "] TEXT PRIMARY KEY)"; + + SQLite::Statement createTableStatement = SQLite::Statement::Create(connection, createTableSQL.str()); + + createTableStatement.Execute(); + + // Create the mapping table + std::ostringstream createMapTableSQL; + createMapTableSQL << "CREATE TABLE [" << tableName << s_OneToManyTable_MapTable_Suffix << "](" + << "[" << s_OneToManyTable_MapTable_ManifestName << "] INT64 NOT NULL," + << '[' << valueName << "] INT64 NOT NULL," + "UNIQUE([" << s_OneToManyTable_MapTable_ManifestName << "], [" << valueName << "]))"; + + SQLite::Statement createMapStatement = SQLite::Statement::Create(connection, createMapTableSQL.str()); + + createMapStatement.Execute(); + + savepoint.Commit(); + } + } +} diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToManyTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToManyTable.h @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "SQLiteWrapper.h" +#include <string_view> + + +namespace AppInstaller::Repository::Microsoft::Schema::V1_0 +{ + namespace details + { + void CreateOneToManyTable(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName); + } + + // A table that represents a value that is 1:N with a manifest. + template <typename TableInfo> + struct OneToManyTable + { + // Creates the table. + static void Create(SQLite::Connection& connection) + { + details::CreateOneToManyTable(connection, TableInfo::TableName(), TableInfo::ValueName()); + } + }; +} diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToOneTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToOneTable.cpp @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "Microsoft/Schema/1_0/OneToOneTable.h" + + +namespace AppInstaller::Repository::Microsoft::Schema::V1_0 +{ + namespace details + { + void CreateOneToOneTable(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName) + { + std::ostringstream createTableSQL; + createTableSQL << "CREATE TABLE [" << tableName << "](" + << '[' << valueName << "] TEXT PRIMARY KEY)"; + + SQLite::Statement createStatement = SQLite::Statement::Create(connection, createTableSQL.str()); + + createStatement.Execute(); + } + } +} diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToOneTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/OneToOneTable.h @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "SQLiteWrapper.h" +#include <string_view> + + +namespace AppInstaller::Repository::Microsoft::Schema::V1_0 +{ + namespace details + { + void CreateOneToOneTable(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName); + } + + // A table that represents a value that is 1:1 with a manifest. + template <typename TableInfo> + struct OneToOneTable + { + // Creates the table. + static void Create(SQLite::Connection& connection) + { + details::CreateOneToOneTable(connection, TableInfo::TableName(), TableInfo::ValueName()); + } + + static std::string_view ValueName() + { + return TableInfo::ValueName(); + } + }; +} diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/PathPartTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/PathPartTable.cpp @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "PathPartTable.h" + + +namespace AppInstaller::Repository::Microsoft::Schema::V1_0 +{ + using namespace std::string_view_literals; + static constexpr std::string_view s_PathPartTable_Table_Name = "pathparts"sv; + static constexpr std::string_view s_PathPartTable_ParentValue_Name = "parent"sv; + static constexpr std::string_view s_PathPartTable_PartValue_Name = "pathpart"sv; + + void PathPartTable::Create(SQLite::Connection& connection) + { + std::ostringstream createTableSQL; + createTableSQL << "CREATE TABLE [" << s_PathPartTable_Table_Name << "](" + << '[' << s_PathPartTable_ParentValue_Name << "] INT64," + << '[' << s_PathPartTable_PartValue_Name << "] TEXT)"; + + SQLite::Statement createStatement = SQLite::Statement::Create(connection, createTableSQL.str()); + + createStatement.Execute(); + } + + std::string_view PathPartTable::ValueName() + { + return s_PathPartTable_PartValue_Name; + } +} diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/PathPartTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/PathPartTable.h @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "SQLiteWrapper.h" +#include <string_view> + + +namespace AppInstaller::Repository::Microsoft::Schema::V1_0 +{ + // A table that represents a single manifest + struct PathPartTable + { + // Creates the table. + static void Create(SQLite::Connection& connection); + + // Gets the value name. + static std::string_view ValueName(); + }; +} diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ProtocolsTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/ProtocolsTable.h @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "Microsoft/Schema/1_0/OneToManyTable.h" + + +namespace AppInstaller::Repository::Microsoft::Schema::V1_0 +{ + namespace details + { + using namespace std::string_view_literals; + + struct ProtocolsTableInfo + { + inline static std::string_view TableName() { return "protocols"sv; } + inline static std::string_view ValueName() { return "protocol"sv; } + }; + } + + // The table for Protocols. + using ProtocolsTable = OneToManyTable<details::ProtocolsTableInfo>; +} diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/TagsTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/TagsTable.h @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "Microsoft/Schema/1_0/OneToManyTable.h" + + +namespace AppInstaller::Repository::Microsoft::Schema::V1_0 +{ + namespace details + { + using namespace std::string_view_literals; + + struct TagsTableInfo + { + inline static std::string_view TableName() { return "tags"sv; } + inline static std::string_view ValueName() { return "tag"sv; } + }; + } + + // The table for Tags. + using TagsTable = OneToManyTable<details::TagsTableInfo>; +} diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/VersionTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_0/VersionTable.h @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "Microsoft/Schema/1_0/OneToOneTable.h" + + +namespace AppInstaller::Repository::Microsoft::Schema::V1_0 +{ + namespace details + { + using namespace std::string_view_literals; + + struct VersionTableInfo + { + inline static std::string_view TableName() { return "versions"sv; } + inline static std::string_view ValueName() { return "version"sv; } + }; + } + + // The table for Version. + using VersionTable = OneToOneTable<details::VersionTableInfo>; +} diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/ISQLiteIndex.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/ISQLiteIndex.h @@ -19,6 +19,9 @@ namespace AppInstaller::Repository::Microsoft::Schema // Gets the schema version that this index interface is built for. virtual Schema::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/MetadataTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/MetadataTable.cpp @@ -6,31 +6,28 @@ namespace AppInstaller::Repository::Microsoft::Schema { - char const* const s_MetadataValueName_MajorVersion = "majorVersion"; - char const* const s_MetadataValueName_MinorVersion = "minorVersion"; - // Table data [note that this table is not versioned, and thus *cannot change*] - static char const* const s_MetadataTable_Table_Name = "metadata"; - static char const* const s_MetadataTable_Column_Name = "name"; - static char const* const s_MetadataTable_Column_Value = "value"; + 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 char const* const s_MetadataTable_Table_Create = R"( + static constexpr std::string_view s_MetadataTable_Table_Create = R"( CREATE TABLE [metadata]( - [name] TEXT PRIMARY KEY NOT NULL UNIQUE, + [name] TEXT PRIMARY KEY NOT NULL, [value] TEXT NOT NULL) -)"; +)"sv; // Statements - static char const* const s_MetadataTableStmt_GetNamedValue = "select [value] from [metadata] where [name] = ?"; - static char const* const s_MetadataTableStmt_SetNamedValue = "insert or replace into [metadata] ([name], [value]) values (?, ?)"; + 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.Step(); + create.Execute(); } - SQLite::Statement MetadataTable::GetNamedValueStatement(SQLite::Connection& connection, const std::string& name) + SQLite::Statement MetadataTable::GetNamedValueStatement(SQLite::Connection& connection, std::string_view name) { THROW_HR_IF(E_INVALIDARG, name.empty()); SQLite::Statement result = SQLite::Statement::Create(connection, s_MetadataTableStmt_GetNamedValue); @@ -39,7 +36,7 @@ CREATE TABLE [metadata]( return result; } - SQLite::Statement MetadataTable::SetNamedValueStatement(SQLite::Connection& connection, const std::string& name) + SQLite::Statement MetadataTable::SetNamedValueStatement(SQLite::Connection& connection, std::string_view name) { THROW_HR_IF(E_INVALIDARG, name.empty()); SQLite::Statement result = SQLite::Statement::Create(connection, s_MetadataTableStmt_SetNamedValue); diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/MetadataTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/MetadataTable.h @@ -4,14 +4,16 @@ #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 - extern char const* const s_MetadataValueName_MajorVersion; - extern char const *const s_MetadataValueName_MinorVersion; + static constexpr std::string_view s_MetadataValueName_MajorVersion = "majorVersion"sv; + static constexpr std::string_view s_MetadataValueName_MinorVersion = "minorVersion"sv; // 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. @@ -21,7 +23,7 @@ namespace AppInstaller::Repository::Microsoft::Schema // Gets the named value from the metadata table, interpreting it as the given type. template <typename Value> - static Value GetNamedValue(SQLite::Connection& connection, const std::string& name) + static Value GetNamedValue(SQLite::Connection& connection, std::string_view name) { SQLite::Statement statement = GetNamedValueStatement(connection, name); return statement.GetColumn<Value>(0); @@ -29,18 +31,18 @@ namespace AppInstaller::Repository::Microsoft::Schema // Sets the named value into the metadata table. template <typename Value> - static void SetNamedValue(SQLite::Connection& connection, const std::string& name, Value&& v) + static void SetNamedValue(SQLite::Connection& connection, std::string_view name, Value&& v) { SQLite::Statement statement = SetNamedValueStatement(connection, name); statement.Bind(2, std::forward<Value>(v)); - THROW_HR_IF(E_UNEXPECTED, statement.Step()); + statement.Execute(); } private: // Internal function that gets the named value. - static SQLite::Statement GetNamedValueStatement(SQLite::Connection& connection, const std::string& name); + static SQLite::Statement GetNamedValueStatement(SQLite::Connection& connection, std::string_view name); // Internal function that sets the named value. - static SQLite::Statement SetNamedValueStatement(SQLite::Connection& connection, const std::string& name); + static SQLite::Statement SetNamedValueStatement(SQLite::Connection& connection, std::string_view name); }; } diff --git a/src/AppInstallerRepositoryCore/SQLiteWrapper.cpp b/src/AppInstallerRepositoryCore/SQLiteWrapper.cpp @@ -8,8 +8,8 @@ // TODO: Invoke the wil error handling callback to log the error #define THROW_SQLITE(_error_) \ do { \ - auto _throw_sqlite_exc = SQLiteException(_error_); \ - throw _throw_sqlite_exc; \ + int _ts_sqliteReturnValue = _error_; \ + THROW_EXCEPTION_MSG(SQLiteException(_ts_sqliteReturnValue), sqlite3_errstr(_ts_sqliteReturnValue)); \ } while (0,0) #define THROW_IF_SQLITE_FAILED(_statement_) \ @@ -25,12 +25,6 @@ namespace AppInstaller::Repository::SQLite { namespace { - class SQLiteErrorCategory : public std::error_category - { - const char* name() const noexcept override { return "sqlite"; } - std::string message(int error) const override { return sqlite3_errstr(error); } - }; - size_t GetNextStatementId() { static std::atomic_size_t statementId(0); @@ -50,6 +44,11 @@ namespace AppInstaller::Repository::SQLite 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) + { + THROW_IF_SQLITE_FAILED(sqlite3_bind_text64(stmt, index, v.data(), v.size(), SQLITE_TRANSIENT, SQLITE_UTF8)); + } + void ParameterSpecificsImpl<int>::Bind(sqlite3_stmt* stmt, int index, int v) { THROW_IF_SQLITE_FAILED(sqlite3_bind_int(stmt, index, v)); @@ -61,12 +60,6 @@ namespace AppInstaller::Repository::SQLite } } - const std::error_category& SQLiteException::GetCategory() noexcept - { - static SQLiteErrorCategory category; - return category; - } - Connection::Connection(const std::string& target, OpenDisposition disposition, OpenFlags flags) { AICLI_LOG(SQL, Info, << "Opening SQLite connection: '" << target << "' [" << std::hex << static_cast<int>(disposition) << ", " << std::hex << static_cast<int>(flags) << "]"); @@ -88,16 +81,28 @@ namespace AppInstaller::Repository::SQLite sqlite3_close_v2(m_dbconn); } - Statement::Statement(Connection& connection, const std::string& sql, bool persistent) + Statement::Statement(Connection& connection, std::string_view sql, bool persistent) { m_id = GetNextStatementId(); AICLI_LOG(SQL, Verbose, << "Preparing statement #" << m_id << ": " << sql); // SQL string size should include the null terminator (https://www.sqlite.org/c3ref/prepare.html) - THROW_IF_SQLITE_FAILED(sqlite3_prepare_v3(connection, sql.c_str(), static_cast<int>(sql.size() + 1), (persistent ? SQLITE_PREPARE_PERSISTENT : 0), &m_stmt, nullptr)); + assert(sql.data()[sql.size()] == '\0'); + THROW_IF_SQLITE_FAILED(sqlite3_prepare_v3(connection, sql.data(), static_cast<int>(sql.size() + 1), (persistent ? SQLITE_PREPARE_PERSISTENT : 0), &m_stmt, nullptr)); } Statement Statement::Create(Connection& connection, const std::string& sql, bool persistent) { + return { connection, { sql.c_str(), sql.size() }, persistent }; + } + + Statement Statement::Create(Connection& connection, std::string_view sql, bool persistent) + { + // 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), persistent); + } + + Statement Statement::Create(Connection& connection, char const* const sql, bool persistent) + { return { connection, sql, persistent }; } @@ -137,6 +142,11 @@ namespace AppInstaller::Repository::SQLite } } + void Statement::Execute(bool failFastOnError) + { + THROW_HR_IF(E_UNEXPECTED, Step(failFastOnError)); + } + void Statement::Reset() { AICLI_LOG(SQL, Verbose, << "Reset statement #" << m_id); @@ -158,7 +168,7 @@ namespace AppInstaller::Repository::SQLite begin.Step(); } - Savepoint Savepoint::Create(Connection& connection, std::string&& name) + Savepoint Savepoint::Create(Connection& connection, std::string name) { return { connection, std::move(name) }; } diff --git a/src/AppInstallerRepositoryCore/SQLiteWrapper.h b/src/AppInstallerRepositoryCore/SQLiteWrapper.h @@ -5,8 +5,7 @@ #include <winsqlite/winsqlite3.h> #include <string> -#include <stdexcept> -#include <system_error> +#include <string_view> #include <tuple> #include <type_traits> #include <utility> @@ -36,6 +35,12 @@ namespace AppInstaller::Repository::SQLite }; template <> + struct ParameterSpecificsImpl<std::string_view> + { + static void Bind(sqlite3_stmt* stmt, int index, std::string_view v); + }; + + template <> struct ParameterSpecificsImpl<int> { static void Bind(sqlite3_stmt* stmt, int index, int v); @@ -47,11 +52,9 @@ namespace AppInstaller::Repository::SQLite } // A SQLite exception. - struct SQLiteException : public std::system_error + struct SQLiteException : public wil::ResultException { - SQLiteException(int error) : std::system_error(error, GetCategory()) {} - - static const std::error_category& GetCategory() noexcept; + SQLiteException(int error) : wil::ResultException(MAKE_HRESULT(SEVERITY_ERROR, FACILITY_SQLITE, error)) {} }; // The connection to a database. @@ -101,6 +104,8 @@ namespace AppInstaller::Repository::SQLite struct Statement { static Statement Create(Connection& connection, const std::string& sql, bool persistent = false); + static Statement Create(Connection& connection, std::string_view sql, bool persistent = false); + static Statement Create(Connection& connection, char const* const sql, bool persistent = false); Statement() = default; @@ -143,6 +148,9 @@ namespace AppInstaller::Repository::SQLite // 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 the value of the specified column from the current row. // The index is 0 based. template <typename Value> @@ -164,7 +172,7 @@ namespace AppInstaller::Repository::SQLite void Reset(); private: - Statement(Connection& connection, const std::string& sql, bool persistent); + Statement(Connection& connection, std::string_view sql, bool persistent); // Helper to receive the integer sequence from the public function. // This is equivalent to calling: @@ -187,7 +195,7 @@ namespace AppInstaller::Repository::SQLite struct Savepoint { // Creates a savepoint, beginning it. - static Savepoint Create(Connection& connection, std::string&& name); + static Savepoint Create(Connection& connection, std::string name); Savepoint(const Savepoint&) = delete; Savepoint& operator=(const Savepoint&) = delete; diff --git a/src/AppInstallerRepositoryCore/pch.h b/src/AppInstallerRepositoryCore/pch.h @@ -14,9 +14,11 @@ #include <winrt/Windows.Foundation.h> +#include <initializer_list> #include <iomanip> #include <string> -#include <stdexcept> +#include <string_view> +#include <sstream> #include <system_error> #include <tuple> #include <type_traits>