winget-cli

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

DependenciesTable.cpp (24284B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #include "pch.h"
      4 #include "DependenciesTable.h"
      5 #include <winget/SQLiteStatementBuilder.h>
      6 #include "winget\DependenciesGraph.h"
      7 #include "Microsoft/Schema/1_0/OneToOneTable.h"
      8 #include "Microsoft/Schema/1_0/IdTable.h"
      9 #include "Microsoft/Schema/1_0/ManifestTable.h"
     10 #include "Microsoft/Schema/1_0/VersionTable.h"
     11 #include "Microsoft/Schema/1_0/Interface.h"
     12 #include "Microsoft/Schema/1_0/ChannelTable.h"
     13 
     14 namespace AppInstaller::Repository::Microsoft::Schema::V1_4
     15 {
     16     using namespace AppInstaller;
     17     using namespace std::string_view_literals;
     18     using namespace SQLite::Builder;
     19     using namespace Schema::V1_0;
     20     using QCol = SQLite::Builder::QualifiedColumn;
     21 
     22     static constexpr std::string_view s_DependenciesTable_Table_Name = "dependencies"sv;
     23     static constexpr std::string_view s_DependenciesTable_Index_Name = "dependencies_pkindex"sv;
     24     static constexpr std::string_view s_DependenciesTable_Manifest_Column_Name = "manifest"sv;
     25     static constexpr std::string_view s_DependenciesTable_MinVersion_Column_Name = "min_version"sv;
     26     static constexpr std::string_view s_DependenciesTable_PackageId_Column_Name = "package_id";
     27 
     28     namespace
     29     {
     30         struct DependencyTableRow 
     31         {
     32             SQLite::rowid_t m_packageRowId;
     33             SQLite::rowid_t m_manifestRowId;
     34 
     35             // Ideally this should be version row id, the version string is more needed than row id,
     36             // this prevents converting back and forth between version row id and version string.
     37             std::optional<Utility::NormalizedString> m_version; 
     38 
     39             bool operator <(const DependencyTableRow& rhs) const
     40             {
     41                 auto lhsVersion = m_version.has_value() ? m_version.value() : "";
     42                 auto rhsVersion = rhs.m_version.has_value() ? rhs.m_version.value() : "";
     43                 return std::tie(m_packageRowId, m_manifestRowId, lhsVersion) < std::tie(rhs.m_packageRowId, rhs.m_manifestRowId, rhsVersion);
     44             }
     45         };
     46 
     47         void ThrowOnMissingPackageNodes(std::vector<Manifest::Dependency>& missingPackageNodes)
     48         {
     49             if (!missingPackageNodes.empty())
     50             {
     51                 std::string missingPackages{ missingPackageNodes.begin()->Id()};
     52                 std::for_each(
     53                     missingPackageNodes.begin() + 1,
     54                     missingPackageNodes.end(),
     55                     [&](auto& dep) { missingPackages.append(", " + dep.Id()); });
     56                 THROW_HR_MSG(APPINSTALLER_CLI_ERROR_MISSING_PACKAGE, "Missing packages: %hs", missingPackages.c_str());
     57             }
     58         }
     59 
     60         std::set<DependencyTableRow> GetAndLinkDependencies(
     61             SQLite::Connection& connection,
     62             const Manifest::Manifest& manifest,
     63             SQLite::rowid_t manifestRowId,
     64             Manifest::DependencyType dependencyType)
     65         {
     66             std::set<DependencyTableRow>  dependencies;
     67             std::vector<Manifest::Dependency> missingPackageNodes;
     68 
     69             for (const auto& installer : manifest.Installers)
     70             {
     71                 installer.Dependencies.ApplyToType(dependencyType, [&](Manifest::Dependency dependency)
     72                 {
     73                     auto packageRowId = IdTable::SelectIdByValue(connection, dependency.Id(), true);
     74                     std::optional<Utility::NormalizedString> version;
     75 
     76                     if (!packageRowId.has_value())
     77                     {
     78                         missingPackageNodes.emplace_back(dependency);
     79                         return;
     80                     }
     81 
     82                     if (dependency.MinVersion.has_value())
     83                     {
     84                         version = dependency.MinVersion.value().ToString();
     85                     }
     86 
     87                     dependencies.emplace(DependencyTableRow{ packageRowId.value(), manifestRowId, version });
     88                 });
     89             }
     90 
     91             ThrowOnMissingPackageNodes(missingPackageNodes);
     92 
     93             return dependencies;
     94         }
     95 
     96         bool RemoveDependenciesByRowIds(SQLite::Connection& connection, std::vector<DependencyTableRow> dependencyTableRows)
     97         {
     98             using namespace SQLite::Builder;
     99             bool tableUpdated = false;
    100             if (dependencyTableRows.empty())
    101             {
    102                 return tableUpdated;
    103             }
    104             SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, std::string{ s_DependenciesTable_Table_Name } + "remove_dependencies_by_rowid");
    105 
    106             SQLite::Builder::StatementBuilder builder;
    107             builder
    108                 .DeleteFrom(s_DependenciesTable_Table_Name)
    109                 .Where(s_DependenciesTable_PackageId_Column_Name).Equals(Unbound)
    110                 .And(s_DependenciesTable_Manifest_Column_Name).Equals(Unbound);
    111 
    112             
    113             SQLite::Statement deleteStmt = builder.Prepare(connection);
    114             for (auto row : dependencyTableRows)
    115             {
    116                 deleteStmt.Reset();
    117                 deleteStmt.Bind(1, row.m_packageRowId);
    118                 deleteStmt.Bind(2, row.m_manifestRowId);
    119                 deleteStmt.Execute();
    120                 tableUpdated = true;
    121             }
    122 
    123             savepoint.Commit();
    124             return tableUpdated;
    125         }
    126 
    127         bool InsertManifestDependencies(
    128             SQLite::Connection& connection,
    129             std::set<DependencyTableRow>& dependenciesTableRows)
    130         {
    131             using namespace SQLite::Builder;
    132             using namespace Schema::V1_0;
    133             bool tableUpdated = false;
    134 
    135             StatementBuilder insertBuilder;
    136             insertBuilder.InsertInto(s_DependenciesTable_Table_Name)
    137                 .Columns({ s_DependenciesTable_Manifest_Column_Name, s_DependenciesTable_MinVersion_Column_Name, s_DependenciesTable_PackageId_Column_Name })
    138                 .Values(Unbound, Unbound, Unbound);
    139             SQLite::Statement insert = insertBuilder.Prepare(connection);
    140 
    141             for (const auto& dep : dependenciesTableRows)
    142             {
    143                 insert.Reset();
    144                 insert.Bind(1, dep.m_manifestRowId);
    145 
    146                 if (dep.m_version.has_value())
    147                 {
    148                     insert.Bind(2, VersionTable::EnsureExists(connection, dep.m_version.value()));
    149                 }
    150                 else
    151                 {
    152                     insert.Bind(2, nullptr);
    153                 }
    154                 
    155                 insert.Bind(3, dep.m_packageRowId);
    156 
    157                 insert.Execute();
    158                 tableUpdated = true;
    159             }
    160 
    161             return tableUpdated;
    162         }
    163     }
    164 
    165     bool DependenciesTable::Exists(const SQLite::Connection& connection)
    166     {
    167         using namespace SQLite;
    168 
    169         Builder::StatementBuilder builder;
    170         builder.Select(Builder::RowCount).From(Builder::Schema::MainTable).
    171             Where(Builder::Schema::TypeColumn).Equals(Builder::Schema::Type_Table).And(Builder::Schema::NameColumn).Equals(s_DependenciesTable_Table_Name);
    172 
    173         Statement statement = builder.Prepare(connection);
    174         THROW_HR_IF(E_UNEXPECTED, !statement.Step());
    175         return statement.GetColumn<int64_t>(0) != 0;
    176     }
    177 
    178     std::string_view DependenciesTable::TableName()
    179     {
    180         return s_DependenciesTable_Table_Name;
    181     }
    182 
    183     void DependenciesTable::Create(SQLite::Connection& connection)
    184     {
    185         using namespace SQLite::Builder;
    186         SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "createDependencyTable_v1_4");
    187         constexpr std::string_view dependencyIndexByVersionId = "dependencies_version_id_index";
    188         constexpr std::string_view dependencyIndexByPackageId = "dependencies_package_id_index";
    189 
    190         StatementBuilder createTableBuilder;
    191         createTableBuilder.CreateTable(TableName()).BeginColumns();
    192         createTableBuilder.Column(IntegerPrimaryKey());
    193 
    194         std::array<DependenciesTableColumnInfo, 2> notNullableDependenciesColumns
    195         {
    196             DependenciesTableColumnInfo{ s_DependenciesTable_Manifest_Column_Name },
    197             DependenciesTableColumnInfo{ s_DependenciesTable_PackageId_Column_Name }
    198         };
    199 
    200         std::array<DependenciesTableColumnInfo, 1> nullableDependenciesColumns
    201         {
    202             DependenciesTableColumnInfo{ s_DependenciesTable_MinVersion_Column_Name }
    203         };
    204 
    205         // Add dependencies column tables not null columns.
    206         for (const DependenciesTableColumnInfo& value : notNullableDependenciesColumns)
    207         {
    208             createTableBuilder.Column(ColumnBuilder(value.Name, Type::RowId).NotNull());
    209         }
    210 
    211         // Add dependencies column tables null columns.
    212         for (const DependenciesTableColumnInfo& value : nullableDependenciesColumns)
    213         {
    214             createTableBuilder.Column(ColumnBuilder(value.Name, Type::RowId));
    215         }
    216 
    217         createTableBuilder.EndColumns();
    218 
    219         createTableBuilder.Execute(connection);
    220 
    221         // Primary key index by package rowid and manifest rowid.
    222         StatementBuilder createPKIndexBuilder;
    223         createPKIndexBuilder.CreateUniqueIndex(s_DependenciesTable_Index_Name).On(s_DependenciesTable_Table_Name).Columns({ s_DependenciesTable_Manifest_Column_Name, s_DependenciesTable_PackageId_Column_Name });
    224         createPKIndexBuilder.Execute(connection);
    225 
    226         // Index of dependency by Manifest id. 
    227         StatementBuilder createIndexByManifestIdBuilder;
    228         createIndexByManifestIdBuilder.CreateIndex(dependencyIndexByVersionId).On(s_DependenciesTable_Table_Name).Columns({ s_DependenciesTable_MinVersion_Column_Name });
    229         createIndexByManifestIdBuilder.Execute(connection);
    230 
    231         // Index of dependency by package id.
    232         StatementBuilder createIndexByPackageIdBuilder;
    233         createIndexByPackageIdBuilder.CreateIndex(dependencyIndexByPackageId).On(s_DependenciesTable_Table_Name).Columns({ s_DependenciesTable_PackageId_Column_Name });
    234         createIndexByPackageIdBuilder.Execute(connection);
    235 
    236         savepoint.Commit();
    237     }
    238 
    239     void DependenciesTable::Drop(SQLite::Connection& connection)
    240     {
    241         SQLite::Builder::StatementBuilder dropTableBuilder;
    242         dropTableBuilder.DropTableIfExists(s_DependenciesTable_Table_Name);
    243 
    244         dropTableBuilder.Execute(connection);
    245     }
    246 
    247     void DependenciesTable::AddDependencies(SQLite::Connection& connection, const Manifest::Manifest& manifest, SQLite::rowid_t manifestRowId)
    248     {
    249         if (!Exists(connection))
    250         {
    251             return;
    252         }
    253 
    254         SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, std::string{ s_DependenciesTable_Table_Name } + "add_dependencies_v1_4");
    255 
    256         auto dependencies = GetAndLinkDependencies(connection, manifest, manifestRowId, Manifest::DependencyType::Package);
    257         if (!dependencies.size())
    258         {
    259             return;
    260         }
    261 
    262         InsertManifestDependencies(connection, dependencies);
    263 
    264         savepoint.Commit();
    265     }
    266 
    267     bool DependenciesTable::UpdateDependencies(SQLite::Connection& connection, const Manifest::Manifest& manifest, SQLite::rowid_t manifestRowId)
    268     {
    269         if (!Exists(connection))
    270         {
    271             return false;
    272         }
    273 
    274         SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, std::string{ s_DependenciesTable_Table_Name } + "update_dependencies_v1_4");
    275 
    276         const auto dependencies = GetAndLinkDependencies(connection, manifest, manifestRowId, Manifest::DependencyType::Package);
    277         auto existingDependencies = GetDependenciesByManifestRowId(connection, manifestRowId);
    278 
    279         // Get dependencies to add.
    280         std::set<DependencyTableRow> toAddDependencies;
    281         std::copy_if(
    282             dependencies.begin(),
    283             dependencies.end(),
    284             std::inserter(toAddDependencies, toAddDependencies.begin()),
    285             [&](DependencyTableRow dep)
    286             {
    287                 Utility::NormalizedString version = dep.m_version.has_value() ? dep.m_version.value() : "";
    288                 return existingDependencies.find(std::make_pair(dep.m_packageRowId, version)) == existingDependencies.end();
    289             }
    290         );
    291 
    292         // Get dependencies to remove.
    293         std::vector<DependencyTableRow> toRemoveDependencies;
    294         std::for_each(
    295             existingDependencies.begin(),
    296             existingDependencies.end(),
    297             [&](std::pair<SQLite::rowid_t, Utility::NormalizedString> row)
    298             {
    299                 if (dependencies.find(DependencyTableRow{row.first, manifestRowId, row.second}) == dependencies.end())
    300                 {
    301                     toRemoveDependencies.emplace_back(DependencyTableRow{ row.first, manifestRowId });
    302                 }
    303             }
    304         );
    305 
    306         bool tableUpdated = RemoveDependenciesByRowIds(connection, toRemoveDependencies);
    307         tableUpdated = InsertManifestDependencies(connection, toAddDependencies) || tableUpdated;
    308         savepoint.Commit();
    309 
    310         return tableUpdated;
    311     }
    312 
    313     void DependenciesTable::RemoveDependencies(SQLite::Connection& connection, SQLite::rowid_t manifestRowId)
    314     {
    315         if (!Exists(connection))
    316         {
    317             return;
    318         }
    319 
    320         SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, std::string{ s_DependenciesTable_Table_Name } + "remove_dependencies_by_manifest_v1_4");
    321 
    322         SQLite::Builder::StatementBuilder builder;
    323         builder.DeleteFrom(s_DependenciesTable_Table_Name).Where(s_DependenciesTable_Manifest_Column_Name).Equals(manifestRowId);
    324 
    325         builder.Execute(connection);
    326         savepoint.Commit();
    327     }
    328 
    329     std::vector<std::pair<SQLite::rowid_t, Utility::NormalizedString>> DependenciesTable::GetDependentsById(const SQLite::Connection& connection, Manifest::string_t packageId)
    330     {
    331         constexpr std::string_view depTableAlias = "dep";
    332         constexpr std::string_view minVersionAlias = "minV";
    333         constexpr std::string_view packageIdAlias = "pId";
    334 
    335 
    336         StatementBuilder builder;
    337         // Find all manifest that depend on this package. Use outer join for joining version table as min_version may be NULL.
    338         // SELECT [dep].[manifest], [dep].[min_version], [pId].[id], [minV].[version] FROM [dependencies] AS [dep] 
    339         // LEFT OUTER JOIN [versions] AS [minV] ON [dep].[min_version] = [minV].[rowid] 
    340         // JOIN [ids] AS [pId] ON [pId].[rowid] = [dep].[package_id] 
    341         // WHERE [pId].[id] = ?
    342         builder.Select()
    343             .Column(QCol(depTableAlias, s_DependenciesTable_Manifest_Column_Name))
    344             .Column(QCol(depTableAlias, s_DependenciesTable_MinVersion_Column_Name))
    345             .Column(QCol(packageIdAlias, IdTable::ValueName()))
    346             .Column(QCol(minVersionAlias, VersionTable::ValueName()))
    347             .From({ s_DependenciesTable_Table_Name }).As(depTableAlias)
    348             .LeftOuterJoin({ VersionTable::TableName() }).As(minVersionAlias)
    349             .On(QCol(depTableAlias, s_DependenciesTable_MinVersion_Column_Name), QCol(minVersionAlias, SQLite::RowIDName))
    350             .Join({ IdTable::TableName() }).As(packageIdAlias)
    351             .On(QCol(packageIdAlias, SQLite::RowIDName), QCol(depTableAlias, s_DependenciesTable_PackageId_Column_Name))
    352             .Where(QCol(packageIdAlias, IdTable::ValueName())).Equals(Unbound);
    353 
    354         SQLite::Statement stmt = builder.Prepare(connection);
    355         stmt.Bind(1, std::string{ packageId });
    356 
    357         std::vector<std::pair<SQLite::rowid_t, Utility::NormalizedString>> resultSet;
    358 
    359         while (stmt.Step())
    360         {
    361             Utility::NormalizedString version = "";
    362             if (!stmt.GetColumnIsNull(1))
    363             {
    364                 // If min_version is not NULL, use the corresponding value from Version table.
    365                 version = stmt.GetColumn<std::string>(3);
    366             }
    367             resultSet.emplace_back(
    368                 std::make_pair(stmt.GetColumn<SQLite::rowid_t>(0), std::move(version)));
    369         }
    370 
    371         return resultSet;
    372     }
    373 
    374     std::set<std::pair<SQLite::rowid_t, Utility::NormalizedString>> DependenciesTable::GetDependenciesByManifestRowId(const SQLite::Connection& connection, SQLite::rowid_t manifestRowId)
    375     {
    376         SQLite::Builder::StatementBuilder builder;
    377 
    378         constexpr std::string_view depTableAlias = "dep";
    379         constexpr std::string_view minVersionAlias = "minV";
    380 
    381         std::set<std::pair<SQLite::rowid_t, Utility::NormalizedString>> resultSet;
    382 
    383         // Use Outer join since min_version could have NULL value. 
    384         // SELECT [dep].[package_id], [dep].[min_version], [minV].[version] FROM [dependencies] AS [dep] 
    385         // LEFT OUTER JOIN [versions] AS [minV] ON [minV].[rowid] = [dep].[min_version]
    386         // WHERE [dep].[manifest] = ?
    387         builder.Select()
    388             .Column(QCol(depTableAlias, s_DependenciesTable_PackageId_Column_Name))
    389             .Column(QCol(depTableAlias, s_DependenciesTable_MinVersion_Column_Name))
    390             .Column(QCol(minVersionAlias, VersionTable::ValueName()))
    391             .From({ s_DependenciesTable_Table_Name }).As(depTableAlias)
    392             .LeftOuterJoin({ VersionTable::TableName() }).As(minVersionAlias)
    393             .On(QCol(minVersionAlias, SQLite::RowIDName), QCol(depTableAlias, s_DependenciesTable_MinVersion_Column_Name))
    394             .Where(QCol(depTableAlias, s_DependenciesTable_Manifest_Column_Name)).Equals(Unbound);
    395 
    396         SQLite::Statement select = builder.Prepare(connection);
    397 
    398         select.Bind(1, manifestRowId);
    399         while (select.Step())
    400         {
    401             Utility::NormalizedString version = "";
    402             if (!select.GetColumnIsNull(1))
    403             {
    404                 // If min_version is not NULL, use the corresponding value from Version table.
    405                 version = select.GetColumn<std::string>(2);
    406             }
    407             resultSet.emplace(std::make_pair(select.GetColumn<SQLite::rowid_t>(0), std::move(version)));
    408         }
    409 
    410         return resultSet;
    411     }
    412 
    413     void DependenciesTable::PrepareForPackaging(SQLite::Connection& connection)
    414     {
    415         SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "prepareForPacking_V1_4");
    416 
    417         StatementBuilder dropIndexBuilder;
    418         dropIndexBuilder.DropIndex({ s_DependenciesTable_Index_Name });
    419         dropIndexBuilder.Execute(connection);
    420 
    421         StatementBuilder dropTableBuilder;
    422         dropTableBuilder.DropTable({ s_DependenciesTable_Table_Name });
    423         dropTableBuilder.Execute(connection);
    424 
    425         savepoint.Commit();
    426     }
    427     
    428     bool DependenciesTable::CheckConsistency(const SQLite::Connection& connection, bool log)
    429     {
    430         StatementBuilder builder;
    431 
    432         if (!Exists(connection))
    433         {
    434             return true;
    435         }
    436 
    437         builder.Select(QCol(s_DependenciesTable_Table_Name, SQLite::RowIDName))
    438             .From(s_DependenciesTable_Table_Name)
    439             .LeftOuterJoin(IdTable::TableName())
    440             .On(QCol(s_DependenciesTable_Table_Name, s_DependenciesTable_PackageId_Column_Name), QCol(IdTable::TableName(), SQLite::RowIDName))
    441             .LeftOuterJoin(ManifestTable::TableName())
    442             .On(QCol(s_DependenciesTable_Table_Name, s_DependenciesTable_Manifest_Column_Name), QCol(ManifestTable::TableName(), SQLite::RowIDName))
    443             .LeftOuterJoin(VersionTable::TableName())
    444             .On(QCol(s_DependenciesTable_Table_Name, s_DependenciesTable_MinVersion_Column_Name), QCol(VersionTable::TableName(), SQLite::RowIDName))
    445             .Where(QCol(ManifestTable::TableName(), SQLite::RowIDName)).IsNull()
    446             .Or(QCol(VersionTable::TableName(), SQLite::RowIDName)).IsNull().And(QCol(s_DependenciesTable_Table_Name, s_DependenciesTable_MinVersion_Column_Name)).IsNotNull()
    447             .Or(QCol(IdTable::TableName(), SQLite::RowIDName)).IsNull();
    448 
    449         SQLite::Statement select = builder.Prepare(connection);
    450         
    451         bool result = true;
    452 
    453         while (select.Step())
    454         {
    455             result = false;
    456 
    457             if (!log)
    458             {
    459                 break;
    460             }
    461 
    462             AICLI_LOG(Repo, Info, << "  [INVALID] row in [" << s_DependenciesTable_Table_Name << "] rowid [" << select.GetColumn<SQLite::rowid_t>(0) << "]");
    463         }
    464 
    465         return result;
    466     }
    467 
    468     bool DependenciesTable::IsValueReferenced(const SQLite::Connection& connection, std::string_view tableName, SQLite::rowid_t valueRowId)
    469     {
    470         if (!Exists(connection))
    471         {
    472             return false;
    473         }
    474 
    475         StatementBuilder builder;
    476 
    477         if (tableName != V1_0::VersionTable::TableName())
    478         {
    479             return false;
    480         }
    481 
    482         std::array<std::string_view, 1> columns = { s_DependenciesTable_MinVersion_Column_Name };
    483         bool referenced = false;
    484 
    485         for(auto column: columns)
    486         {
    487             builder.Select(SQLite::RowIDName).From(s_DependenciesTable_Table_Name).Where(column).Equals(Unbound).Limit(1);
    488 
    489             SQLite::Statement select = builder.Prepare(connection);
    490 
    491             select.Bind(1, valueRowId);
    492             if (select.Step())
    493             {
    494                 referenced = true;
    495                 break;
    496             }
    497         }
    498 
    499         return referenced;
    500     }
    501 
    502     std::vector<SQLite::rowid_t> DependenciesTable::GetDependenciesMinVersionsRowIdByManifestId(const SQLite::Connection& connection, SQLite::rowid_t manifestRowId)
    503     {
    504         if (!Exists(connection))
    505         {
    506             return {};
    507         }
    508 
    509         StatementBuilder builder;
    510 
    511         std::vector<SQLite::rowid_t> result;
    512 
    513         // Find all versions for manifest row.
    514         // SELECT [min_version] FROM [dependencies]  
    515         // WHERE [manifest] = ?
    516         builder.Select()
    517             .Column(s_DependenciesTable_MinVersion_Column_Name)
    518             .From({ s_DependenciesTable_Table_Name })
    519             .Where(s_DependenciesTable_Manifest_Column_Name).Equals(Unbound);
    520 
    521         auto select = builder.Prepare(connection);
    522 
    523         select.Bind(1, manifestRowId);
    524 
    525         while (select.Step())
    526         {
    527             if (!select.GetColumnIsNull(0))
    528             {
    529                 result.emplace_back(select.GetColumn<SQLite::rowid_t>(0));
    530             }
    531         }
    532 
    533         return result;
    534     }
    535 
    536     std::vector<std::pair<SQLite::rowid_t, Utility::NormalizedString>> DependenciesTable::GetAllDependenciesWithMinVersions(const SQLite::Connection& connection)
    537     {
    538         if (!Exists(connection))
    539         {
    540             return {};
    541         }
    542 
    543         std::vector<std::pair<SQLite::rowid_t, Utility::NormalizedString>> result;
    544 
    545         constexpr std::string_view depTableAlias = "dep";
    546         constexpr std::string_view minVersionAlias = "minV";
    547 
    548         StatementBuilder builder;
    549 
    550         // SELECT [dep].[package_id], [minV].[version] FROM [dependencies] AS [dep] 
    551         // JOIN [versions] AS [minV] ON [minV].[rowid] = [dep].[min_version]
    552         builder.Select()
    553             .Column(QCol(depTableAlias, s_DependenciesTable_PackageId_Column_Name))
    554             .Column(QCol(minVersionAlias, VersionTable::ValueName()))
    555             .From({ s_DependenciesTable_Table_Name }).As(depTableAlias)
    556             .Join({ VersionTable::TableName() }).As(minVersionAlias)
    557             .On(QCol(minVersionAlias, SQLite::RowIDName), QCol(depTableAlias, s_DependenciesTable_MinVersion_Column_Name));
    558 
    559         SQLite::Statement select = builder.Prepare(connection);
    560 
    561         while (select.Step())
    562         {
    563             Utility::NormalizedString version = "";
    564             if (!select.GetColumnIsNull(1))
    565             {
    566                 version = select.GetColumn<std::string>(1);
    567             }
    568 
    569             if (!version.empty())
    570             {
    571                 result.emplace_back(std::make_pair(select.GetColumn<SQLite::rowid_t>(0), version));
    572             }
    573         }
    574 
    575         return result;
    576     }
    577 }