PackagesTable.cpp (12022B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 #include "pch.h" 4 #include "PackagesTable.h" 5 #include <winget/SQLiteStatementBuilder.h> 6 #include "OneToManyTableWithMap.h" 7 8 9 namespace AppInstaller::Repository::Microsoft::Schema::V2_0 10 { 11 using namespace std::string_view_literals; 12 static constexpr std::string_view s_PackagesTable_Table_Name = "packages"sv; 13 static constexpr std::string_view s_PackagesTable_Index_Separator = "_"sv; 14 static constexpr std::string_view s_PackagesTable_Index_Suffix = "_index"sv; 15 16 namespace details 17 { 18 void PackagesTableCreate(SQLite::Connection& connection, std::initializer_list<ColumnInfo> values) 19 { 20 using namespace SQLite::Builder; 21 22 SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createPackagesTable_v2_0"); 23 24 StatementBuilder createTableBuilder; 25 createTableBuilder.CreateTable(s_PackagesTable_Table_Name).BeginColumns(); 26 27 // Add an integer primary key to keep the manifest rowid consistent 28 createTableBuilder.Column(IntegerPrimaryKey()); 29 30 for (const ColumnInfo& value : values) 31 { 32 ColumnBuilder columnBuilder(value.Name, value.Type); 33 34 if (!value.AllowNull) 35 { 36 columnBuilder.NotNull(); 37 } 38 39 createTableBuilder.Column(columnBuilder); 40 } 41 42 createTableBuilder.EndColumns(); 43 44 createTableBuilder.Execute(connection); 45 46 // Create a unique index with the primary key values 47 StatementBuilder pkIndexBuilder; 48 49 pkIndexBuilder.CreateUniqueIndex({ s_PackagesTable_Table_Name, s_PackagesTable_Index_Suffix }).On(s_PackagesTable_Table_Name).BeginColumns(); 50 51 for (const ColumnInfo& value : values) 52 { 53 if (value.PrimaryKey) 54 { 55 pkIndexBuilder.Column(value.Name); 56 } 57 } 58 59 pkIndexBuilder.EndColumns(); 60 61 pkIndexBuilder.Execute(connection); 62 63 // Create an index on every value to improve performance 64 for (const ColumnInfo& value : values) 65 { 66 StatementBuilder createIndexBuilder; 67 68 createIndexBuilder.CreateIndex({ s_PackagesTable_Table_Name, s_PackagesTable_Index_Separator, value.Name, s_PackagesTable_Index_Suffix }); 69 createIndexBuilder.On(s_PackagesTable_Table_Name).Columns(value.Name); 70 71 createIndexBuilder.Execute(connection); 72 } 73 74 savepoint.Commit(); 75 } 76 77 // Creates a statement and executes it, select the actual values for a given manifest id. 78 // Ex. 79 // SELECT [ids].[id] FROM [manifest] 80 // JOIN [ids] ON [manifest].[id] = [ids].[rowid] 81 // WHERE [manifest].[rowid] = 1 82 SQLite::Statement PackagesTableGetValuesById_Statement( 83 const SQLite::Connection& connection, 84 SQLite::rowid_t id, 85 std::initializer_list<std::string_view> columns, 86 bool stepAndVerify) 87 { 88 SQLite::Builder::StatementBuilder builder; 89 builder.Select(columns).From(s_PackagesTable_Table_Name).Where(SQLite::RowIDName).Equals(id); 90 91 SQLite::Statement result = builder.Prepare(connection); 92 93 if (stepAndVerify) 94 { 95 THROW_HR_IF(E_NOT_SET, !result.Step()); 96 } 97 98 return result; 99 } 100 101 SQLite::Statement PackagesTableUpdateValueIdById_Statement(SQLite::Connection& connection, std::string_view valueName) 102 { 103 SQLite::Builder::StatementBuilder builder; 104 builder.Update(s_PackagesTable_Table_Name).Set().Column(valueName).Equals(SQLite::Builder::Unbound).Where(SQLite::RowIDName).Equals(SQLite::Builder::Unbound); 105 106 return builder.Prepare(connection); 107 } 108 109 void PackagesTablePrepareForPackaging(SQLite::Connection& connection, std::initializer_list<ColumnInfo> values) 110 { 111 SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "pfpPackagesTable_v2_0"); 112 113 // Drop the index on the requested values 114 for (const auto& value : values) 115 { 116 SQLite::Builder::StatementBuilder dropIndexBuilder; 117 dropIndexBuilder.DropIndex({ s_PackagesTable_Table_Name, s_PackagesTable_Index_Separator, value.Name, s_PackagesTable_Index_Suffix }); 118 119 dropIndexBuilder.Execute(connection); 120 } 121 122 SQLite::Builder::StatementBuilder dropPKIndexBuilder; 123 dropPKIndexBuilder.DropIndex({ s_PackagesTable_Table_Name, s_PackagesTable_Index_Suffix }); 124 dropPKIndexBuilder.Execute(connection); 125 126 savepoint.Commit(); 127 } 128 129 bool PackagesTableCheckColumnForNulls(const SQLite::Connection& connection, std::string_view valueName, bool log) 130 { 131 // Build a select statement to find values that contain an embedded null character 132 // Such as: 133 // Select count(*) from table where instr(value,char(0))>0 134 SQLite::Builder::StatementBuilder builder; 135 builder. 136 Select({ SQLite::RowIDName, valueName }). 137 From(s_PackagesTable_Table_Name). 138 WhereValueContainsEmbeddedNullCharacter(valueName); 139 140 SQLite::Statement select = builder.Prepare(connection); 141 bool result = true; 142 143 while (select.Step()) 144 { 145 result = false; 146 147 if (!log) 148 { 149 break; 150 } 151 152 AICLI_LOG(Repo, Info, << " [INVALID] value [" << valueName << "] in table [" << s_PackagesTable_Table_Name << 153 "] at row [" << select.GetColumn<SQLite::rowid_t>(0) << "] contains an embedded null character and starts with [" << 154 select.GetColumn<std::string>(1) << "]"); 155 } 156 157 return result; 158 } 159 160 bool PackagesTableCheckConsistency(const SQLite::Connection& connection, std::initializer_list<std::string_view> values, bool log) 161 { 162 bool result = true; 163 164 for (const auto& value : values) 165 { 166 if (result || log) 167 { 168 result = details::PackagesTableCheckColumnForNulls(connection, value, log) && result; 169 } 170 } 171 172 return result; 173 } 174 } 175 176 std::string_view PackagesTable::TableName() 177 { 178 return s_PackagesTable_Table_Name; 179 } 180 181 void PackagesTable::Drop(SQLite::Connection& connection) 182 { 183 SQLite::Builder::StatementBuilder dropTableBuilder; 184 dropTableBuilder.DropTable(s_PackagesTable_Table_Name); 185 186 dropTableBuilder.Execute(connection); 187 } 188 189 bool PackagesTable::Exists(const SQLite::Connection& connection) 190 { 191 using namespace SQLite; 192 193 Builder::StatementBuilder builder; 194 builder.Select(Builder::RowCount).From(Builder::Schema::MainTable). 195 Where(Builder::Schema::TypeColumn).Equals(Builder::Schema::Type_Table).And(Builder::Schema::NameColumn).Equals(s_PackagesTable_Table_Name); 196 197 Statement statement = builder.Prepare(connection); 198 THROW_HR_IF(E_UNEXPECTED, !statement.Step()); 199 return statement.GetColumn<int64_t>(0) != 0; 200 } 201 202 void PackagesTable::AddColumn(SQLite::Connection& connection, const ColumnInfo& value) 203 { 204 using namespace SQLite::Builder; 205 206 SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "addColumnPackagesTable_v2_0"); 207 208 StatementBuilder alterTableBuilder; 209 alterTableBuilder.AlterTable(s_PackagesTable_Table_Name).Add(value.Name, value.Type); 210 211 alterTableBuilder.Execute(connection); 212 213 savepoint.Commit(); 214 } 215 216 SQLite::rowid_t PackagesTable::Insert(SQLite::Connection& connection, const std::vector<NameValuePair>& values) 217 { 218 SQLite::Builder::StatementBuilder builder; 219 builder.InsertInto(s_PackagesTable_Table_Name).BeginColumns(); 220 221 for (const NameValuePair& value : values) 222 { 223 builder.Column(value.Name); 224 } 225 226 builder.EndColumns().BeginValues(); 227 228 for (const NameValuePair& value : values) 229 { 230 builder.Value(value.Value); 231 } 232 233 builder.EndValues(); 234 235 builder.Execute(connection); 236 237 return connection.GetLastInsertRowID(); 238 } 239 240 bool PackagesTable::ExistsById(const SQLite::Connection& connection, SQLite::rowid_t id) 241 { 242 SQLite::Builder::StatementBuilder builder; 243 builder.Select(SQLite::Builder::RowCount).From(s_PackagesTable_Table_Name).Where(SQLite::RowIDName).Equals(id); 244 245 SQLite::Statement countStatement = builder.Prepare(connection); 246 247 THROW_HR_IF(E_UNEXPECTED, !countStatement.Step()); 248 249 return (countStatement.GetColumn<int>(0) != 0); 250 } 251 252 std::vector<SQLite::rowid_t> PackagesTable::GetAllRowIds(const SQLite::Connection& connection, std::string_view orderByColumn, size_t limit) 253 { 254 SQLite::Builder::StatementBuilder selectBuilder; 255 selectBuilder.Select(SQLite::RowIDName).From(s_PackagesTable_Table_Name).OrderBy(orderByColumn); 256 257 if (limit) 258 { 259 selectBuilder.Limit(limit); 260 } 261 262 SQLite::Statement select = selectBuilder.Prepare(connection); 263 264 std::vector<SQLite::rowid_t> result; 265 while (select.Step()) 266 { 267 result.emplace_back(select.GetColumn<SQLite::rowid_t>(0)); 268 } 269 return result; 270 } 271 272 uint64_t PackagesTable::GetCount(const SQLite::Connection& connection) 273 { 274 SQLite::Builder::StatementBuilder builder; 275 builder.Select(SQLite::Builder::RowCount).From(s_PackagesTable_Table_Name); 276 277 SQLite::Statement countStatement = builder.Prepare(connection); 278 279 THROW_HR_IF(E_UNEXPECTED, !countStatement.Step()); 280 281 return static_cast<uint64_t>(countStatement.GetColumn<SQLite::rowid_t>(0)); 282 } 283 284 int PackagesTable::BuildSearchStatement( 285 SQLite::Builder::StatementBuilder& builder, 286 std::string_view valueName, 287 std::string_view primaryAlias, 288 std::string_view valueAlias, 289 bool useLike) 290 { 291 using QCol = SQLite::Builder::QualifiedColumn; 292 293 // Build a statement like: 294 // SELECT packages.rowid as p, packages.id as v from packages 295 // where packages.id = <value> 296 builder.Select(). 297 Column(QCol(s_PackagesTable_Table_Name, SQLite::RowIDName)).As(primaryAlias). 298 Column(QCol(s_PackagesTable_Table_Name, valueName)).As(valueAlias). 299 From(s_PackagesTable_Table_Name).Where(QCol(s_PackagesTable_Table_Name, valueName)); 300 301 int result = -1; 302 303 if (useLike) 304 { 305 builder.Like(SQLite::Builder::Unbound); 306 result = builder.GetLastBindIndex(); 307 builder.Escape(SQLite::EscapeCharForLike); 308 } 309 else 310 { 311 builder.Equals(SQLite::Builder::Unbound); 312 result = builder.GetLastBindIndex(); 313 } 314 315 return result; 316 } 317 318 bool PackagesTable::IsEmpty(SQLite::Connection& connection) 319 { 320 SQLite::Builder::StatementBuilder builder; 321 builder.Select(SQLite::Builder::RowCount).From(s_PackagesTable_Table_Name); 322 323 SQLite::Statement countStatement = builder.Prepare(connection); 324 325 THROW_HR_IF(E_UNEXPECTED, !countStatement.Step()); 326 327 return (countStatement.GetColumn<int>(0) == 0); 328 } 329 }