From f5adc6c0ee7965abcad4cc73d0f36d1ed3cba3cc Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 29 Sep 2015 11:29:50 +0200 Subject: Complete pkg-status, rework object model --- bpkg/package | 232 +++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 140 insertions(+), 92 deletions(-) (limited to 'bpkg/package') diff --git a/bpkg/package b/bpkg/package index 2601b7b..a75d18e 100644 --- a/bpkg/package +++ b/bpkg/package @@ -12,6 +12,8 @@ #include +#include + #include #include @@ -19,23 +21,6 @@ namespace bpkg { - // Use an image type to map version to the database since there - // is no way to modify individual components directly. - // - #pragma db value - struct _version - { - std::uint16_t epoch; - string upstream; - std::uint16_t revision; - string canonical_upstream; - }; -} - -#include - -namespace bpkg -{ // compare_lazy_ptr // // Compare two lazy pointers via the pointed-to object ids. @@ -50,6 +35,7 @@ namespace bpkg } }; + // path // using optional_string = optional; @@ -70,15 +56,74 @@ namespace bpkg to((?) ? (?)->string () : bpkg::optional_string ()) \ from((?) ? bpkg::dir_path (*(?)) : bpkg::optional_dir_path ()) + // version // + // Sometimes we need to split the version into two parts: the part + // that goes into the object id (epoch, canonical upstream, revision) + // and the original upstream. This is what the canonical_version and + // upstream_version value types are for. Note that upstream_version + // derives from version and uses it as storage. The idea here is this: + // when we split the version, we often still want to have the "whole" + // version object readily accessible and that's exactly what this + // strange contraption is for. See available_package for an example + // on how everything fits together. + // + // + #pragma db value + struct canonical_version + { + uint16_t epoch; + string canonical_upstream; + uint16_t revision; + }; + + #pragma db value transient + struct upstream_version: version + { + #pragma db member(upstream) virtual(string) \ + get(this.upstream ()) \ + set(this = bpkg::version (0, std::move (?), 0)) + + upstream_version () = default; + upstream_version (version v): version (move (v)) {} + upstream_version& + operator= (version v) {version& b (*this); b = v; return *this;} + + void + init (const canonical_version& cv, const upstream_version& uv) + { + *this = version (cv.epoch, uv.upstream (), cv.revision); + assert (cv.canonical_upstream == canonical_upstream ()); + } + }; + + // Use an image type to map version to the database since there + // is no way to modify individual components directly. + // + #pragma db value + struct _version + { + uint16_t epoch; + string canonical_upstream; + uint16_t revision; + string upstream; + }; + #pragma db map type(version) as(_version) \ to(bpkg::_version{(?).epoch (), \ - (?).upstream (), \ + (?).canonical_upstream (), \ (?).revision (), \ - (?).canonical_upstream ()}) \ + (?).upstream ()}) \ from(bpkg::version ((?).epoch, std::move ((?).upstream), (?).revision)) + + // repository_location + // + #pragma db map type(repository_location) as(string) \ + to((?).string ()) from(bpkg::repository_location (?)) + + // repository // #pragma db object pointer(std::shared_ptr) session @@ -86,20 +131,18 @@ namespace bpkg { public: // We use a weak pointer for prerequisite repositories because we - // might have cycles. + // could have cycles. No cycles in complements, thought. // using complements_type = std::set, compare_lazy_ptr>; using prerequisites_type = std::set, compare_lazy_ptr>; + string name; // Object id (canonical name). repository_location location; complements_type complements; prerequisites_type prerequisites; - const string& - name () const {return location.canonical_name ();} - // Used to detect recursive fecthing. Will probably be replaced // by the 'repositories' file timestamp or hashsum later. // @@ -108,27 +151,18 @@ namespace bpkg public: explicit - repository (repository_location l): location (move (l)) {} + repository (repository_location l): location (move (l)) + { + name = location.canonical_name (); + } // Database mapping. // - #pragma db value - struct _id_type - { - string name; // Canonical name. - string location; - }; - - _id_type - _id () const; - - void - _id (_id_type&&); - - #pragma db member(location) transient + #pragma db member(name) id - #pragma db member(id) virtual(_id_type) before id(name) \ - get(_id) set(_id (std::move (?))) column("") + #pragma db member(location) \ + set(this.location = std::move (?); \ + assert (this.name == this.location.canonical_name ())) #pragma db member(complements) id_column("repository") \ value_column("complement") value_not_null @@ -141,35 +175,15 @@ namespace bpkg repository () = default; }; - #pragma db view object(repository) query(repository::id.name != "" && (?)) + #pragma db view object(repository) query(repository::name != "" && (?)) struct repository_count { - #pragma db column("count(" + repository::id.name + ")") + #pragma db column("count(*)") size_t result; operator size_t () const {return result;} }; - // package_version_id - // - #pragma db value - struct package_version_id - { - string name; - std::uint16_t epoch; - string upstream; // Canonical upstream. - std::uint16_t revision; - - package_version_id () = default; - package_version_id (string, const version&); - - #pragma db member(epoch) column("version_epoch") - #pragma db member(upstream) column("version_upstream") - #pragma db member(revision) column("version_revision") - }; - - bool - operator< (const package_version_id&, const package_version_id&); // package_location // @@ -182,50 +196,44 @@ namespace bpkg path location; // Relative to the repository. }; + // available_package // + #pragma db value + struct available_package_id + { + string name; + canonical_version version; + + available_package_id () = default; + available_package_id (string, const bpkg::version&); + }; + + bool + operator< (const available_package_id&, const available_package_id&); + #pragma db object pointer(shared_ptr) session class available_package { public: - using version_type = bpkg::version; - - string name; - version_type version; + available_package_id id; + upstream_version version; // List of repositories to which this package version belongs (yes, // in our world, it can be in multiple, unrelated repositories). // std::vector locations; - // Database mapping. - // + public: + available_package (string name, bpkg::version v) + : id (move (name), v), version (move (v)) {} - // id + // Database mapping. // - #pragma db value - struct _id_type - { - package_version_id data; - string version_original_upstream; - - #pragma db member(data) column("") - }; - - _id_type - _id () const; - - void - _id (_id_type&&); - - #pragma db member(name) transient - #pragma db member(version) transient + #pragma db member(id) id column("") - #pragma db member(id) virtual(_id_type) before id(data) \ - get(_id) set(_id (std::move (?))) column("") + #pragma db member(version) set(this.version.init (this.id.version, (?))) - // repositories - // #pragma db member(locations) id_column("") value_column("") \ unordered value_not_null @@ -237,12 +245,13 @@ namespace bpkg #pragma db view object(available_package) struct available_package_count { - #pragma db column("count(" + available_package::id.data.name + ")") + #pragma db column("count(*)") size_t result; operator size_t () const {return result;} }; + // state // enum class state @@ -266,6 +275,7 @@ namespace bpkg to(to_string (?)) \ from(bpkg::from_string (?)) + // package // #pragma db object pointer(shared_ptr) session @@ -275,7 +285,7 @@ namespace bpkg using version_type = bpkg::version; using state_type = bpkg::state; - string name; + string name; // Object id. version_type version; state_type state; @@ -314,6 +324,44 @@ namespace bpkg friend class odb::access; package () = default; }; + + // Version comparison operators. + // + // @@ Still not sure if this is conceptually the right way to do + // it (should we document it as an advanced technique?). Also + // the return type (query_base) ugliness. + // + template + inline auto + operator== (const T& x, const version& y) -> decltype (x.epoch == 0) + { + return x.epoch == y.epoch () && + x.canonical_upstream == y.canonical_upstream () && + x.revision == y.revision (); + } + + template + inline auto + operator> (const T& x, const version& y) -> decltype (x.epoch > 0) + { + return x.epoch > y.epoch () || + (x.epoch == y.epoch () && + x.canonical_upstream > y.canonical_upstream ()) || + (x.epoch == y.epoch () && + x.canonical_upstream == y.canonical_upstream () && + x.revision > y.revision ()); + } + + template + inline auto + order_by_version_desc (const T& x) -> //decltype ("ORDER BY" + x.epoch) + decltype (x.epoch == 0) + { + return "ORDER BY" + + x.epoch + "DESC," + + x.canonical_upstream + "DESC," + + x.revision + "DESC"; + } } #include -- cgit v1.1