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 }