winget-cli

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

OneToOneTable.cpp (8671B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #include "pch.h"
      4 #include "Microsoft/Schema/1_0/OneToOneTable.h"
      5 #include "Microsoft/Schema/1_0/ManifestTable.h"
      6 #include <winget/SQLiteStatementBuilder.h>
      7 
      8 
      9 namespace AppInstaller::Repository::Microsoft::Schema::V1_0
     10 {
     11     namespace details
     12     {
     13         using namespace std::string_view_literals;
     14         static constexpr std::string_view s_OneToOneTable_IndexSuffix = "_pkindex"sv;
     15 
     16         void CreateOneToOneTable(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, bool useNamedIndices)
     17         {
     18             using namespace SQLite::Builder;
     19 
     20             // Starting in V1.1, all code should be going this route of creating named indices rather than using primary or unique keys on columns.
     21             // The resulting database will function the same, but give us control to drop the indices to reduce space.
     22             if (useNamedIndices)
     23             {
     24                 SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, std::string{ tableName } + "_create_v1_1");
     25 
     26                 StatementBuilder createTableBuilder;
     27 
     28                 createTableBuilder.CreateTable(tableName).Columns({
     29                     IntegerPrimaryKey(),
     30                     ColumnBuilder(valueName, Type::Text).NotNull()
     31                     });
     32 
     33                 createTableBuilder.Execute(connection);
     34 
     35                 StatementBuilder indexBuilder;
     36                 indexBuilder.CreateUniqueIndex({ tableName, s_OneToOneTable_IndexSuffix }).On(tableName).Columns(valueName);
     37                 indexBuilder.Execute(connection);
     38 
     39                 savepoint.Commit();
     40             }
     41             else
     42             {
     43                 StatementBuilder createTableBuilder;
     44 
     45                 createTableBuilder.CreateTable(tableName).Columns({
     46                     ColumnBuilder(valueName, Type::Text).NotNull().PrimaryKey()
     47                     });
     48 
     49                 createTableBuilder.Execute(connection);
     50             }
     51         }
     52 
     53         void DropOneToOneTable(SQLite::Connection& connection, std::string_view tableName)
     54         {
     55             SQLite::Builder::StatementBuilder dropTableBuilder;
     56             dropTableBuilder.DropTable(tableName);
     57 
     58             dropTableBuilder.Execute(connection);
     59         }
     60 
     61         std::optional<SQLite::rowid_t> OneToOneTableSelectIdByValue(const SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, std::string_view value, bool useLike)
     62         {
     63             SQLite::Builder::StatementBuilder selectBuilder;
     64             selectBuilder.Select(SQLite::RowIDName).From(tableName).Where(valueName);
     65 
     66             if (useLike)
     67             {
     68                 selectBuilder.LikeWithEscape(value);
     69             }
     70             else
     71             {
     72                 selectBuilder.Equals(value);
     73             }
     74 
     75             SQLite::Statement select = selectBuilder.Prepare(connection);
     76 
     77             if (select.Step())
     78             {
     79                 return select.GetColumn<SQLite::rowid_t>(0);
     80             }
     81             else
     82             {
     83                 return {};
     84             }
     85         }
     86 
     87         std::optional<std::string> OneToOneTableSelectValueById(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, SQLite::rowid_t id)
     88         {
     89             SQLite::Builder::StatementBuilder selectBuilder;
     90             selectBuilder.Select(valueName).From(tableName).Where(SQLite::RowIDName).Equals(id);
     91 
     92             SQLite::Statement select = selectBuilder.Prepare(connection);
     93 
     94             if (select.Step())
     95             {
     96                 return select.GetColumn<std::string>(0);
     97             }
     98             else
     99             {
    100                 return {};
    101             }
    102         }
    103 
    104         std::vector<SQLite::rowid_t> OneToOneTableGetAllRowIds(const SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, size_t limit)
    105         {
    106             SQLite::Builder::StatementBuilder selectBuilder;
    107             selectBuilder.Select(SQLite::RowIDName).From(tableName).OrderBy(valueName);
    108 
    109             if (limit)
    110             {
    111                 selectBuilder.Limit(limit);
    112             }
    113 
    114             SQLite::Statement select = selectBuilder.Prepare(connection);
    115 
    116             std::vector<SQLite::rowid_t> result;
    117             while (select.Step())
    118             {
    119                 result.emplace_back(select.GetColumn<SQLite::rowid_t>(0));
    120             }
    121             return result;
    122         }
    123 
    124         SQLite::rowid_t OneToOneTableEnsureExists(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, std::string_view value, bool overwriteLikeMatch)
    125         {
    126             auto selectResult = OneToOneTableSelectIdByValue(connection, tableName, valueName, value, overwriteLikeMatch);
    127             if (selectResult)
    128             {
    129                 if (overwriteLikeMatch)
    130                 {
    131                     // If the value in the table is not an exact match, overwrite it with the incoming value
    132                     auto tableValue = OneToOneTableSelectValueById(connection, tableName, valueName, selectResult.value());
    133                     if (tableValue.value() != value)
    134                     {
    135                         SQLite::Builder::StatementBuilder updateBuilder;
    136                         updateBuilder.Update(tableName).Set().Column(valueName).Equals(value).Where(SQLite::RowIDName).Equals(selectResult);
    137 
    138                         updateBuilder.Execute(connection);
    139                     }
    140                 }
    141 
    142                 return selectResult.value();
    143             }
    144 
    145             SQLite::Builder::StatementBuilder insertBuilder;
    146             insertBuilder.InsertInto(tableName).Columns(valueName).Values(value);
    147 
    148             insertBuilder.Execute(connection);
    149 
    150             return connection.GetLastInsertRowID();
    151         }
    152 
    153         void OneToOneTablePrepareForPackaging(SQLite::Connection& connection, std::string_view tableName, bool useNamedIndices, bool preserveValuesIndex)
    154         {
    155             if (useNamedIndices && !preserveValuesIndex)
    156             {
    157                 SQLite::Builder::StatementBuilder dropIndexBuilder;
    158                 dropIndexBuilder.DropIndex({ tableName, s_OneToOneTable_IndexSuffix });
    159                 dropIndexBuilder.Execute(connection);
    160             }
    161         }
    162 
    163         uint64_t OneToOneTableGetCount(const SQLite::Connection& connection, std::string_view tableName)
    164         {
    165             SQLite::Builder::StatementBuilder builder;
    166             builder.Select(SQLite::Builder::RowCount).From(tableName);
    167 
    168             SQLite::Statement countStatement = builder.Prepare(connection);
    169 
    170             THROW_HR_IF(E_UNEXPECTED, !countStatement.Step());
    171 
    172             return static_cast<uint64_t>(countStatement.GetColumn<SQLite::rowid_t>(0));
    173         }
    174 
    175         bool OneToOneTableIsEmpty(SQLite::Connection& connection, std::string_view tableName)
    176         {
    177             return (OneToOneTableGetCount(connection, tableName) == 0);
    178         }
    179 
    180         void OneToOneTableDeleteById(SQLite::Connection& connection, std::string_view tableName, SQLite::rowid_t id)
    181         {
    182             SQLite::Builder::StatementBuilder builder;
    183             builder.DeleteFrom(tableName).Where(SQLite::RowIDName).Equals(id);
    184 
    185             builder.Execute(connection);
    186         }
    187 
    188         bool OneToOneTableCheckConsistency(const SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, bool log)
    189         {
    190             // Build a select statement to find values that contain an embedded null character
    191             // Such as:
    192             // Select count(*) from table where instr(value,char(0))>0
    193             SQLite::Builder::StatementBuilder builder;
    194             builder.
    195                 Select({ SQLite::RowIDName, valueName }).
    196                 From(tableName).
    197                 WhereValueContainsEmbeddedNullCharacter(valueName);
    198 
    199             SQLite::Statement select = builder.Prepare(connection);
    200             bool result = true;
    201 
    202             while (select.Step())
    203             {
    204                 result = false;
    205 
    206                 if (!log)
    207                 {
    208                     break;
    209                 }
    210 
    211                 AICLI_LOG(Repo, Info, << "  [INVALID] value in table [" << tableName << "] at row [" << select.GetColumn<SQLite::rowid_t>(0) << "] contains an embedded null character and starts with [" << select.GetColumn<std::string>(1) << "]");
    212             }
    213 
    214             return result;
    215         }
    216     }
    217 }