aboutsummaryrefslogtreecommitdiff
path: root/mod/mod-builds.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'mod/mod-builds.cxx')
-rw-r--r--mod/mod-builds.cxx250
1 files changed, 175 insertions, 75 deletions
diff --git a/mod/mod-builds.cxx b/mod/mod-builds.cxx
index fbdae4c..30d3696 100644
--- a/mod/mod-builds.cxx
+++ b/mod/mod-builds.cxx
@@ -5,6 +5,7 @@
#include <mod/mod-builds.hxx>
#include <set>
+#include <algorithm> // find_if()
#include <libstudxml/serializer.hxx>
@@ -441,37 +442,167 @@ handle (request& rq, response& rs)
s << DIV_COUNTER (build_count, "Build", "Builds");
};
+ // We will not display hidden configurations, unless the configuration is
+ // specified explicitly.
+ //
+ cstrings conf_names;
+
+ if (params.configuration ().empty () ||
+ params.configuration ().find_first_of ("*?") != string::npos)
+ {
+ for (const auto& c: *build_conf_map_)
+ {
+ if (belongs (*c.second, "all"))
+ conf_names.push_back (c.first);
+ }
+ }
+ else
+ conf_names = *build_conf_names_;
+
size_t count;
size_t page (params.page ());
- if (params.result () != "unbuilt")
+ if (params.result () != "unbuilt") // Print package build configurations.
{
- transaction t (build_db_->begin ());
+ // It seems impossible to filter out the package-excluded configuration
+ // builds via the database query. Thus, we will traverse through builds
+ // that pass the form filter and match them against expressions and
+ // constraints of a package they are builds of.
+ //
+ // We will calculate the total builds count and cache build objects for
+ // printing on the same pass. Note that we need to print the count before
+ // printing the builds.
+ //
+ count = 0;
+ vector<shared_ptr<build>> builds;
- count = build_db_->query_value<package_build_count> (
- build_query<package_build_count> (
- *build_conf_names_, params, tn, nullopt /* archived */));
+ // Prepare the package build prepared query.
+ //
+ using query = query<package_build>;
+ using prep_query = prepared_query<package_build>;
- // Print the filter form.
+ query q (build_query<package_build> (
+ conf_names, params, tn, nullopt /* archived */));
+
+ // Specify the portion. Note that we will be querying builds in chunks,
+ // not to hold locks for too long.
//
- print_form (query_toolchains (), count);
+ size_t offset (0);
// Print package build configurations ordered by the timestamp (later goes
// first).
//
+ q += "ORDER BY" + query::build::timestamp + "DESC" +
+ "OFFSET" + query::_ref (offset) + "LIMIT 50";
+
+ connection_ptr conn (build_db_->connection ());
+
+ prep_query pq (
+ conn->prepare_query<package_build> ("mod-builds-query", q));
+
+ // Note that we can't skip the proper number of builds in the database
+ // query for a page numbers greater than one. So we will query builds from
+ // the very beginning and skip the appropriate number of them while
+ // iterating through the query result.
+ //
+ size_t skip (page * page_configs);
+ size_t print (page_configs);
+
+ // Note that adjacent builds may well relate to the same package. We will
+ // use this fact for a cheap optimization, loading the build package only
+ // if it differs from the previous one.
+ //
+ shared_ptr<build_package> p;
+
+ for (bool ne (true); ne; )
+ {
+ transaction t (conn->begin ());
+
+ // Query package builds (and cache the result).
+ //
+ auto bs (pq.execute ());
+
+ if ((ne = !bs.empty ()))
+ {
+ offset += bs.size ();
+
+ // Iterate over builds and cache build objects that should be printed.
+ // Skip the appropriate number of them (for page number greater than
+ // one).
+ //
+ for (auto& pb: bs)
+ {
+ shared_ptr<build>& b (pb.build);
+
+ // Prior to loading the package object check if it is already
+ // loaded.
+ //
+ if (p == nullptr || p->id != b->id.package)
+ p = build_db_->load<build_package> (b->id.package);
+
+ auto i (build_conf_map_->find (b->configuration.c_str ()));
+ assert (i != build_conf_map_->end ());
+
+ // Match the configuration against the package build
+ // expressions/constraints.
+ //
+ if (!exclude (*p, *i->second))
+ {
+ if (skip != 0)
+ --skip;
+ else if (print != 0)
+ {
+ // As we query builds in multiple transactions we may see the
+ // same build multiple times. Let's skip the duplicates. Note:
+ // we don't increment the counter in this case.
+ //
+ if (find_if (builds.begin (),
+ builds.end (),
+ [&b] (const shared_ptr<build>& pb)
+ {
+ return b->id == pb->id;
+ }) != builds.end ())
+ continue;
+
+ if (b->state == build_state::built)
+ {
+ build_db_->load (*b, b->results_section);
+
+ // Let's clear unneeded result logs for builds being cached.
+ //
+ for (operation_result& r: b->results)
+ r.log.clear ();
+ }
+
+ builds.push_back (move (b));
+
+ --print;
+ }
+
+ ++count;
+ }
+ }
+ }
+
+ // Print the filter form after the build count is calculated. Note:
+ // query_toolchains() must be called inside the build db transaction.
+ //
+ else
+ print_form (query_toolchains (), count);
+
+ t.commit ();
+ }
+
+ // Finally, print the cached package build configurations.
+ //
timestamp now (system_clock::now ());
// Enclose the subsequent tables to be able to use nth-child CSS selector.
//
s << DIV;
- for (auto& pb: build_db_->query<package_build> (
- build_query<package_build> (
- *build_conf_names_, params, tn, nullopt /* archived */) +
- "ORDER BY" + query<package_build>::build::timestamp + "DESC" +
- "OFFSET" + to_string (page * page_configs) +
- "LIMIT" + to_string (page_configs)))
+ for (const shared_ptr<build>& pb: builds)
{
- build& b (*pb.build);
+ const build& b (*pb);
string ts (butl::to_string (b.timestamp,
"%Y-%m-%d %H:%M:%S %Z",
@@ -479,9 +610,6 @@ handle (request& rq, response& rs)
true) +
" (" + butl::to_string (now - b.timestamp, false) + " ago)");
- if (b.state == build_state::built)
- build_db_->load (b, b.results_section);
-
s << TABLE(CLASS="proplist build")
<< TBODY
<< TR_NAME (b.package_name, string (), root, b.tenant)
@@ -505,8 +633,6 @@ handle (request& rq, response& rs)
<< ~TABLE;
}
s << ~DIV;
-
- t.commit ();
}
else // Print unbuilt package configurations.
{
@@ -609,12 +735,12 @@ handle (request& rq, response& rs)
//
// Note that we also need to deduct the package-excluded configurations
// count from the maximum possible number of unbuilt configurations. The
- // only way to achieve this is to traverse through the build-constrained
- // packages and match their constraints against our configurations.
+ // only way to achieve this is to traverse through the packages and
+ // match their build expressions/constraints against our configurations.
//
// Also note that some existing builds can now be excluded by packages
- // due to the build configuration target change. We should deduct such
- // builds count from the number of existing package builds.
+ // due to the build configuration target or class set change. We should
+ // deduct such builds count from the number of existing package builds.
//
size_t nmax (
config_toolchains.size () *
@@ -624,7 +750,7 @@ handle (request& rq, response& rs)
size_t ncur = build_db_->query_value<package_build_count> (
build_query<package_build_count> (
- *build_conf_names_, bld_params, tn, false /* archived */));
+ conf_names, bld_params, tn, false /* archived */));
// From now we will be using specific package name and version for each
// build database query.
@@ -665,23 +791,23 @@ handle (request& rq, response& rs)
size_t nt (tc == "*" ? toolchains.size () : 1);
- // The number of build-constrained packages can potentially be large,
- // and we may implement some caching in the future. However, the
- // caching will not be easy as the cached values depend on the filter
- // form parameters.
+ // The number of packages can potentially be large, and we may
+ // implement some caching in the future. However, the caching will not
+ // be easy as the cached values depend on the filter form parameters.
//
- query<build_constrained_package> q (
- package_query<build_constrained_package> (
+ query<buildable_package> q (
+ package_query<buildable_package> (
params, tn, false /* archived */));
- for (const auto& p: build_db_->query<build_constrained_package> (q))
+ for (auto& bp: build_db_->query<buildable_package> (q))
{
- const build_package& bp (*p.package);
- id = bp.id;
+ id = move (bp.id);
+
+ shared_ptr<build_package> p (build_db_->load<build_package> (id));
for (const auto& c: configs)
{
- if (exclude (bp, *c))
+ if (exclude (*p, *c))
{
nmax -= nt;
@@ -706,9 +832,10 @@ handle (request& rq, response& rs)
//
// 1: package name
// 2: package version (descending)
- // 3: configuration name
- // 4: toolchain name
- // 5: toolchain version (descending)
+ // 3: package tenant
+ // 4: configuration name
+ // 5: toolchain name
+ // 6: toolchain version (descending)
//
// Prepare the build package prepared query.
//
@@ -764,30 +891,12 @@ handle (request& rq, response& rs)
// Note that the query already constrains the tenant via the build
// package id.
//
- build_query<package_build> (*build_conf_names_,
- bld_params,
- nullopt /* tenant */,
- false /* archived */));
+ build_query<package_build> (
+ conf_names, bld_params, nullopt /* tenant */, false /* archived */));
prep_bld_query bld_prep_query (
conn->prepare_query<package_build> ("mod-builds-build-query", bq));
- // Prepare the build-constrained package prepared query.
- //
- // For each build-constrained package we will exclude the corresponding
- // configurations from being printed.
- //
- using ctr_query = query<build_constrained_package>;
- using prep_ctr_query = prepared_query<build_constrained_package>;
-
- ctr_query cq (
- package_id_eq<build_constrained_package> (
- ctr_query::build_package::id, id));
-
- prep_ctr_query ctr_prep_query (
- conn->prepare_query<build_constrained_package> (
- "mod-builds-build-constrained-package-query", cq));
-
size_t skip (page * page_configs);
size_t print (page_configs);
@@ -816,29 +925,20 @@ handle (request& rq, response& rs)
id = move (p.id);
// Copy configuration/toolchain combinations for this package,
- // skipping explicitly excluded configurations.
+ // skipping excluded configurations.
//
set<config_toolchain> unbuilt_configs;
{
- build_constrained_package p;
- if (ctr_prep_query.execute_one (p))
+ shared_ptr<build_package> p (build_db_->load<build_package> (id));
+
+ for (const auto& ct: config_toolchains)
{
- const build_package& bp (*p.package);
- for (const auto& ct: config_toolchains)
- {
- auto i (build_conf_map_->find (ct.configuration.c_str ()));
- assert (i != build_conf_map_->end ());
+ auto i (build_conf_map_->find (ct.configuration.c_str ()));
+ assert (i != build_conf_map_->end ());
- if (!exclude (bp, *i->second))
- unbuilt_configs.insert (ct);
- }
+ if (!exclude (*p, *i->second))
+ unbuilt_configs.insert (ct);
}
- else
- // For libc++, the set's copy-assignment operator requires the
- // element type to be copy-assignable, for some reason.
- //
- unbuilt_configs.insert (config_toolchains.begin (),
- config_toolchains.end ());
}
// Iterate through the package configuration builds and erase them
@@ -856,7 +956,7 @@ handle (request& rq, response& rs)
//
for (const auto& ct: unbuilt_configs)
{
- if (skip > 0)
+ if (skip != 0)
{
--skip;
continue;