winget-cli

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

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 }