winget-cli

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

StatusItemTable.cpp (18516B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #include "pch.h"
      4 #include "StatusItemTable.h"
      5 #include "Database/Schema/0_1/SetInfoTable.h"
      6 #include <AppInstallerDateTime.h>
      7 #include <AppInstallerLanguageUtilities.h>
      8 #include <AppInstallerStrings.h>
      9 #include <winget/SQLiteStatementBuilder.h>
     10 
     11 using namespace AppInstaller::SQLite;
     12 using namespace AppInstaller::SQLite::Builder;
     13 using namespace AppInstaller::Utility;
     14 
     15 namespace winrt::Microsoft::Management::Configuration::implementation::Database::Schema::V0_3
     16 {
     17     namespace
     18     {
     19         constexpr std::string_view s_StatusItemTable_Table = "status_items"sv;
     20         constexpr std::string_view s_StatusItemTable_ChangeIdentifierIndex = "status_items_change_idx"sv;
     21         constexpr std::string_view s_StatusItemTable_SetRowIdIndex = "status_items_set_idx"sv;
     22         constexpr std::string_view s_StatusItemTable_UnitInstanceIndex = "status_items_unit_idx"sv;
     23 
     24         constexpr std::string_view s_StatusItemTable_Column_ChangeIdentifier = "change_identifier"sv;
     25         constexpr std::string_view s_StatusItemTable_Column_ChangeTimeInitial = "change_time_initial"sv;
     26         constexpr std::string_view s_StatusItemTable_Column_ChangeTimeLatest = "change_time_latest"sv;
     27         constexpr std::string_view s_StatusItemTable_Column_SetRowId = "set_rowid"sv;
     28         constexpr std::string_view s_StatusItemTable_Column_InQueue = "in_queue"sv;
     29         constexpr std::string_view s_StatusItemTable_Column_UnitInstanceIdentifier = "unit_instance_identifier"sv;
     30         constexpr std::string_view s_StatusItemTable_Column_State = "state"sv;
     31         constexpr std::string_view s_StatusItemTable_Column_ResultCode = "result_code"sv;
     32         constexpr std::string_view s_StatusItemTable_Column_ResultDescription = "result_description"sv;
     33         constexpr std::string_view s_StatusItemTable_Column_ResultDetails = "result_details"sv;
     34         constexpr std::string_view s_StatusItemTable_Column_ResultSource = "result_source"sv;
     35 
     36         void BuildBaseStatusSelectStatement(StatementBuilder& builder)
     37         {
     38             builder.Select({
     39                 s_StatusItemTable_Column_ChangeIdentifier,          // 0
     40                 s_StatusItemTable_Column_ChangeTimeLatest,          // 1
     41                 V0_1::SetInfoTable::InstanceIdentifierColumn(),     // 2
     42                 s_StatusItemTable_Column_InQueue,                   // 3
     43                 s_StatusItemTable_Column_UnitInstanceIdentifier,    // 4
     44                 s_StatusItemTable_Column_State,                     // 5
     45                 s_StatusItemTable_Column_ResultCode,                // 6
     46                 s_StatusItemTable_Column_ResultDescription,         // 7
     47                 s_StatusItemTable_Column_ResultDetails,             // 8
     48                 s_StatusItemTable_Column_ResultSource,              // 9
     49             }).From(s_StatusItemTable_Table).LeftOuterJoin(V0_1::SetInfoTable::TableName()).On(QualifiedColumn{ s_StatusItemTable_Table, s_StatusItemTable_Column_SetRowId }, QualifiedColumn{ V0_1::SetInfoTable::TableName(), RowIDName });
     50         }
     51 
     52         IConfigurationDatabase::StatusItemTuple GetTupleFromStatement(Statement& statement)
     53         {
     54             return std::make_tuple(
     55                 statement.GetColumn<int64_t>(0),
     56                 ConvertUnixEpochToSystemClock(statement.GetColumn<int64_t>(1)),
     57                 statement.GetColumn<GUID>(2),
     58                 statement.GetColumn<bool>(3),
     59                 statement.GetColumnIsNull(4) ? std::nullopt : std::make_optional<GUID>(statement.GetColumn<GUID>(4)),
     60                 statement.GetColumn<int32_t>(5),
     61                 statement.GetColumnIsNull(6) ? std::nullopt : std::make_optional<HRESULT>(statement.GetColumn<int32_t>(6)),
     62                 statement.GetColumnIsNull(7) ? std::string{} : statement.GetColumn<std::string>(7),
     63                 statement.GetColumnIsNull(8) ? std::string{} : statement.GetColumn<std::string>(8),
     64                 statement.GetColumnIsNull(9) ? ConfigurationUnitResultSource::None : statement.GetColumn<ConfigurationUnitResultSource>(9)
     65             );
     66         }
     67 
     68         int64_t GetLatestChangeIdentifier(Connection& connection)
     69         {
     70             StatementBuilder getLatestChangeBuilder;
     71             getLatestChangeBuilder.Select().Column(Aggregate::Max, s_StatusItemTable_Column_ChangeIdentifier).From(s_StatusItemTable_Table);
     72 
     73             Statement getLatestChange = getLatestChangeBuilder.Prepare(connection);
     74 
     75             return (getLatestChange.Step() ? getLatestChange.GetColumn<int64_t>(0) : 0);
     76         }
     77 
     78         int64_t GetNextChangeIdentifier(Connection& connection)
     79         {
     80             return GetLatestChangeIdentifier(connection) + 1;
     81         }
     82 
     83         void UpdateStatus(
     84             Connection& connection,
     85             const GUID& setInstanceIdentifier,
     86             const std::optional<int32_t>& state,
     87             const std::optional<bool>& inQueue,
     88             const std::optional<GUID>& unitInstanceIdentifier = std::nullopt,
     89             const std::optional<int32_t>& resultCode = std::nullopt,
     90             const std::optional<std::string>& resultDescription = std::nullopt,
     91             const std::optional<std::string>& resultDetails = std::nullopt,
     92             const std::optional<ConfigurationUnitResultSource>& resultSource = std::nullopt)
     93         {
     94             static constexpr std::string_view s_alias = "sub_expression";
     95 
     96             int64_t changeIdentifier = GetNextChangeIdentifier(connection);
     97             int64_t changeTime = GetCurrentUnixEpoch();
     98 
     99             // Statement like:
    100             // Update status_items set state = 1 from (Select rowid from set_info where instance_identifier = "foo") as sub where status_items.set_rowid = sub.rowid
    101             StatementBuilder updateBuilder;
    102             updateBuilder.Update(s_StatusItemTable_Table).Set();
    103 
    104             if (state)
    105             {
    106                 updateBuilder.Column(s_StatusItemTable_Column_State).Equals(state.value());
    107             }
    108 
    109             if (inQueue)
    110             {
    111                 updateBuilder.Column(s_StatusItemTable_Column_InQueue).Equals(inQueue.value());
    112             }
    113 
    114             if (resultCode)
    115             {
    116                 updateBuilder.Column(s_StatusItemTable_Column_ResultCode).Equals(resultCode.value());
    117             }
    118 
    119             if (resultDescription)
    120             {
    121                 updateBuilder.Column(s_StatusItemTable_Column_ResultDescription).Equals(resultDescription.value());
    122             }
    123 
    124             if (resultDetails)
    125             {
    126                 updateBuilder.Column(s_StatusItemTable_Column_ResultDetails).Equals(resultDetails.value());
    127             }
    128 
    129             if (resultSource)
    130             {
    131                 updateBuilder.Column(s_StatusItemTable_Column_ResultSource).Equals(resultSource.value());
    132             }
    133 
    134             updateBuilder.
    135                 Column(s_StatusItemTable_Column_ChangeIdentifier).Equals(changeIdentifier).
    136                 Column(s_StatusItemTable_Column_ChangeTimeLatest).Equals(changeTime).
    137             From().BeginParenthetical().
    138                 Select(RowIDName).From(V0_1::SetInfoTable::TableName()).Where(V0_1::SetInfoTable::InstanceIdentifierColumn()).Equals(setInstanceIdentifier).
    139             EndParenthetical().As(s_alias).Where(QualifiedColumn{ s_StatusItemTable_Table, s_StatusItemTable_Column_SetRowId }).Equals(QualifiedColumn{ s_alias, RowIDName }).
    140                 And(QualifiedColumn{ s_StatusItemTable_Table, s_StatusItemTable_Column_UnitInstanceIdentifier }).Equals(unitInstanceIdentifier);
    141 
    142             updateBuilder.Execute(connection);
    143 
    144             if (connection.GetChanges() == 0)
    145             {
    146                 // No change; we need to insert the status row
    147                 StatementBuilder insertBuilder;
    148                 insertBuilder.InsertInto(s_StatusItemTable_Table).Columns({
    149                     s_StatusItemTable_Column_ChangeIdentifier,
    150                     s_StatusItemTable_Column_ChangeTimeInitial,
    151                     s_StatusItemTable_Column_ChangeTimeLatest,
    152                     s_StatusItemTable_Column_SetRowId,
    153                     s_StatusItemTable_Column_InQueue,
    154                     s_StatusItemTable_Column_UnitInstanceIdentifier,
    155                     s_StatusItemTable_Column_State,
    156                     s_StatusItemTable_Column_ResultCode,
    157                     s_StatusItemTable_Column_ResultDescription,
    158                     s_StatusItemTable_Column_ResultDetails,
    159                     s_StatusItemTable_Column_ResultSource,
    160                 }).Select().
    161                     Value(changeIdentifier).
    162                     Value(changeTime).
    163                     Value(changeTime).
    164                     Column(QualifiedColumn{ V0_1::SetInfoTable::TableName(), RowIDName }).
    165                     Value(inQueue.value_or(false)).
    166                     Value(unitInstanceIdentifier).
    167                     Value(state.value_or(0)).
    168                     Value(resultCode).
    169                     Value(resultDescription).
    170                     Value(resultDetails).
    171                     Value(resultSource).
    172                 From(V0_1::SetInfoTable::TableName()).Where(QualifiedColumn{ V0_1::SetInfoTable::TableName(), V0_1::SetInfoTable::InstanceIdentifierColumn() }).Equals(setInstanceIdentifier);
    173 
    174                 insertBuilder.Execute(connection);
    175             }
    176         }
    177 
    178         Statement PrepareSelectStatusValues(Connection& connection, const std::optional<GUID>& setInstanceIdentifier, const std::optional<GUID>& unitInstanceIdentifier, std::initializer_list<std::string_view> columns)
    179         {
    180             THROW_HR_IF(E_INVALIDARG, (setInstanceIdentifier && unitInstanceIdentifier) || (!setInstanceIdentifier && !unitInstanceIdentifier));
    181 
    182             StatementBuilder builder;
    183             builder.Select(columns).From(s_StatusItemTable_Table);
    184 
    185             if (setInstanceIdentifier)
    186             {
    187                 builder.Join(V0_1::SetInfoTable::TableName()).On(QualifiedColumn{ s_StatusItemTable_Table, s_StatusItemTable_Column_SetRowId }, QualifiedColumn{ V0_1::SetInfoTable::TableName(), RowIDName }).
    188                     Where(QualifiedColumn{ V0_1::SetInfoTable::TableName(), V0_1::SetInfoTable::InstanceIdentifierColumn() }).Equals(setInstanceIdentifier).
    189                     And(s_StatusItemTable_Column_UnitInstanceIdentifier).IsNull();
    190             }
    191             else
    192             {
    193                 builder.Where(s_StatusItemTable_Column_UnitInstanceIdentifier).Equals(unitInstanceIdentifier.value());
    194             }
    195 
    196             return builder.Prepare(connection);
    197         }
    198     }
    199 
    200     StatusItemTable::StatusItemTable(Connection& connection) : m_connection(connection) {}
    201 
    202     void StatusItemTable::Create()
    203     {
    204         Savepoint savepoint = Savepoint::Create(m_connection, "StatusItemTable_Create_0_3");
    205 
    206         StatementBuilder tableBuilder;
    207         tableBuilder.CreateTable(s_StatusItemTable_Table).Columns({
    208             IntegerPrimaryKey(),
    209             ColumnBuilder(s_StatusItemTable_Column_ChangeIdentifier, Type::Int64).NotNull(),
    210             ColumnBuilder(s_StatusItemTable_Column_ChangeTimeInitial, Type::Int64).NotNull(),
    211             ColumnBuilder(s_StatusItemTable_Column_ChangeTimeLatest, Type::Int64).NotNull(),
    212             ColumnBuilder(s_StatusItemTable_Column_SetRowId, Type::RowId).NotNull(),
    213             ColumnBuilder(s_StatusItemTable_Column_InQueue, Type::Bool).NotNull(),
    214             ColumnBuilder(s_StatusItemTable_Column_UnitInstanceIdentifier, Type::Blob),
    215             ColumnBuilder(s_StatusItemTable_Column_State, Type::Int).NotNull(),
    216             ColumnBuilder(s_StatusItemTable_Column_ResultCode, Type::Int),
    217             ColumnBuilder(s_StatusItemTable_Column_ResultDescription, Type::Text),
    218             ColumnBuilder(s_StatusItemTable_Column_ResultDetails, Type::Text),
    219             ColumnBuilder(s_StatusItemTable_Column_ResultSource, Type::Int),
    220         });
    221 
    222         tableBuilder.Execute(m_connection);
    223 
    224         {
    225             StatementBuilder indexBuilder;
    226             indexBuilder.CreateIndex(s_StatusItemTable_ChangeIdentifierIndex).On(s_StatusItemTable_Table).Columns(s_StatusItemTable_Column_ChangeIdentifier);
    227             indexBuilder.Execute(m_connection);
    228         }
    229 
    230         {
    231             StatementBuilder indexBuilder;
    232             indexBuilder.CreateIndex(s_StatusItemTable_SetRowIdIndex).On(s_StatusItemTable_Table).Columns(s_StatusItemTable_Column_SetRowId);
    233             indexBuilder.Execute(m_connection);
    234         }
    235 
    236         {
    237             StatementBuilder indexBuilder;
    238             indexBuilder.CreateUniqueIndex(s_StatusItemTable_UnitInstanceIndex).On(s_StatusItemTable_Table).Columns(s_StatusItemTable_Column_UnitInstanceIdentifier);
    239             indexBuilder.Execute(m_connection);
    240         }
    241 
    242         savepoint.Commit();
    243     }
    244 
    245     void StatusItemTable::RemoveForSet(AppInstaller::SQLite::rowid_t target)
    246     {
    247         StatementBuilder builder;
    248         builder.DeleteFrom(s_StatusItemTable_Table).Where(s_StatusItemTable_Column_SetRowId).Equals(target);
    249         builder.Execute(m_connection);
    250     }
    251 
    252     std::vector<IConfigurationDatabase::StatusItemTuple> StatusItemTable::GetStatusSince(int64_t changeIdentifier)
    253     {
    254         StatementBuilder builder;
    255         BuildBaseStatusSelectStatement(builder);
    256         builder.Where(s_StatusItemTable_Column_ChangeIdentifier).IsGreaterThan(changeIdentifier).OrderBy(s_StatusItemTable_Column_ChangeIdentifier);
    257 
    258         Statement statement = builder.Prepare(m_connection);
    259 
    260         std::vector<IConfigurationDatabase::StatusItemTuple> result;
    261 
    262         while (statement.Step())
    263         {
    264             result.emplace_back(GetTupleFromStatement(statement));
    265         }
    266 
    267         return result;
    268     }
    269 
    270     std::tuple<int64_t, std::vector<IConfigurationDatabase::StatusItemTuple>> StatusItemTable::GetStatusBaseline()
    271     {
    272         int64_t latestChange = GetLatestChangeIdentifier(m_connection);
    273         std::vector<IConfigurationDatabase::StatusItemTuple> setStatus;
    274 
    275         StatementBuilder builder;
    276         BuildBaseStatusSelectStatement(builder);
    277         builder.Where(s_StatusItemTable_Column_UnitInstanceIdentifier).IsNull();
    278 
    279         Statement statement = builder.Prepare(m_connection);
    280 
    281         while (statement.Step())
    282         {
    283             setStatus.emplace_back(GetTupleFromStatement(statement));
    284         }
    285 
    286         return std::make_tuple(latestChange, std::move(setStatus));
    287     }
    288 
    289     void StatusItemTable::UpdateSetState(const guid& setInstanceIdentifier, ConfigurationSetState state)
    290     {
    291         UpdateStatus(m_connection, setInstanceIdentifier, AppInstaller::ToIntegral(state), std::nullopt);
    292     }
    293 
    294     void StatusItemTable::UpdateSetInQueue(const guid& setInstanceIdentifier, bool inQueue)
    295     {
    296         UpdateStatus(m_connection, setInstanceIdentifier, std::nullopt, inQueue);
    297     }
    298 
    299     void StatusItemTable::UpdateUnitState(const guid& setInstanceIdentifier, const IConfigurationDatabase::ConfigurationSetChangeDataPtr& changeData)
    300     {
    301         const auto& resultInformation = changeData->ResultInformation();
    302 
    303         std::optional<HRESULT> resultCode;
    304         std::optional<std::string> resultDescription;
    305         std::optional<std::string> resultDetails;
    306         std::optional<ConfigurationUnitResultSource> resultSource;
    307 
    308         if (resultInformation)
    309         {
    310             resultCode = resultInformation.ResultCode();
    311             resultDescription = ConvertToUTF8(resultInformation.Description());
    312             resultDetails = ConvertToUTF8(resultInformation.Details());
    313             resultSource = resultInformation.ResultSource();
    314         }
    315 
    316         UpdateStatus(m_connection, setInstanceIdentifier, AppInstaller::ToIntegral(changeData->UnitState()), std::nullopt, changeData->Unit().InstanceIdentifier(), resultCode, resultDescription, resultDetails, resultSource);
    317     }
    318 
    319     ConfigurationSetState StatusItemTable::GetSetState(const guid& instanceIdentifier)
    320     {
    321         Statement statement = PrepareSelectStatusValues(m_connection, instanceIdentifier, std::nullopt, { s_StatusItemTable_Column_State });
    322 
    323         return (statement.Step() ? statement.GetColumn<ConfigurationSetState>(0) : ConfigurationSetState::Unknown);
    324     }
    325 
    326     std::chrono::system_clock::time_point StatusItemTable::GetSetApplyBegun(const GUID& instanceIdentifier)
    327     {
    328         Statement statement = PrepareSelectStatusValues(m_connection, instanceIdentifier, std::nullopt, { s_StatusItemTable_Column_ChangeTimeInitial });
    329 
    330         return (statement.Step() ? ConvertUnixEpochToSystemClock(statement.GetColumn<int64_t>(0)) : std::chrono::system_clock::time_point{});
    331     }
    332 
    333     std::chrono::system_clock::time_point StatusItemTable::GetSetApplyEnded(const GUID& instanceIdentifier)
    334     {
    335         Statement statement = PrepareSelectStatusValues(m_connection, instanceIdentifier, std::nullopt, { s_StatusItemTable_Column_ChangeTimeLatest, s_StatusItemTable_Column_InQueue });
    336 
    337         // Only return the end time if no longer in the queue
    338         if (statement.Step() && !statement.GetColumn<bool>(1))
    339         {
    340             return ConvertUnixEpochToSystemClock(statement.GetColumn<int64_t>(0));
    341         }
    342 
    343         return std::chrono::system_clock::time_point{};
    344     }
    345 
    346     ConfigurationUnitState StatusItemTable::GetUnitState(const guid& instanceIdentifier)
    347     {
    348         Statement statement = PrepareSelectStatusValues(m_connection, std::nullopt, instanceIdentifier, { s_StatusItemTable_Column_State });
    349 
    350         return (statement.Step() ? statement.GetColumn<ConfigurationUnitState>(0) : ConfigurationUnitState::Unknown);
    351     }
    352 
    353     std::optional<std::tuple<HRESULT, std::string, std::string, ConfigurationUnitResultSource>> StatusItemTable::GetUnitResultInformation(const guid& instanceIdentifier)
    354     {
    355         Statement statement = PrepareSelectStatusValues(m_connection, std::nullopt, instanceIdentifier,
    356             { s_StatusItemTable_Column_ResultCode, s_StatusItemTable_Column_ResultDescription, s_StatusItemTable_Column_ResultDetails, s_StatusItemTable_Column_ResultSource });
    357 
    358         if (statement.Step() && !statement.GetColumnIsNull(0))
    359         {
    360             return statement.GetRow<int32_t, std::string, std::string, ConfigurationUnitResultSource>();
    361         }
    362 
    363         return std::nullopt;
    364     }
    365 }