diff options
Diffstat (limited to 'libbrep')
-rw-r--r-- | libbrep/.gitignore | 10 | ||||
-rw-r--r-- | libbrep/build.cxx | 48 | ||||
-rw-r--r-- | libbrep/build.hxx | 166 | ||||
-rw-r--r-- | libbrep/build.xml | 67 | ||||
-rw-r--r-- | libbrep/buildfile | 50 | ||||
-rw-r--r-- | libbrep/common.cxx | 10 | ||||
-rw-r--r-- | libbrep/common.hxx | 341 | ||||
-rw-r--r-- | libbrep/database-lock.cxx | 44 | ||||
-rw-r--r-- | libbrep/database-lock.hxx | 43 | ||||
-rwxr-xr-x | libbrep/odb.sh | 40 | ||||
-rw-r--r-- | libbrep/package-extra.sql | 130 | ||||
-rw-r--r-- | libbrep/package-traits.cxx | 70 | ||||
-rw-r--r-- | libbrep/package-traits.hxx | 38 | ||||
-rw-r--r-- | libbrep/package.cxx | 169 | ||||
-rw-r--r-- | libbrep/package.hxx | 500 | ||||
-rw-r--r-- | libbrep/package.xml | 417 | ||||
-rw-r--r-- | libbrep/types.hxx | 94 | ||||
-rw-r--r-- | libbrep/utility.hxx | 34 | ||||
-rw-r--r-- | libbrep/version.hxx.in | 74 | ||||
-rw-r--r-- | libbrep/wrapper-traits.hxx | 63 |
20 files changed, 2408 insertions, 0 deletions
diff --git a/libbrep/.gitignore b/libbrep/.gitignore new file mode 100644 index 0000000..3f251f5 --- /dev/null +++ b/libbrep/.gitignore @@ -0,0 +1,10 @@ +common-odb.?xx + +package-odb.?xx +package.sql +package-extra.hxx + +build-odb.?xx +build.sql + +version.hxx diff --git a/libbrep/build.cxx b/libbrep/build.cxx new file mode 100644 index 0000000..710c0b2 --- /dev/null +++ b/libbrep/build.cxx @@ -0,0 +1,48 @@ +// file : libbrep/build.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include <libbrep/build.hxx> + +namespace brep +{ + // build_state + // + string + to_string (build_state s) + { + switch (s) + { + case build_state::untested: return "untested"; + case build_state::testing: return "testing"; + case build_state::tested: return "tested"; + } + + return string (); // Should never reach. + } + + build_state + to_build_state (const string& s) + { + if (s == "untested") return build_state::untested; + else if (s == "testing") return build_state::testing; + else if (s == "tested") return build_state::tested; + else throw invalid_argument ("invalid build state '" + s + "'"); + } + + // build + // + build:: + build (string pnm, version pvr, string cfg, string mnm, string msm) + : id (package_id (move (pnm), pvr), move (cfg)), + package_name (id.package.name), + package_version (move (pvr)), + configuration (id.configuration), + state (build_state::testing), + timestamp (timestamp_type::clock::now ()), + forced (false), + machine (move (mnm)), + machine_summary (move (msm)) + { + } +} diff --git a/libbrep/build.hxx b/libbrep/build.hxx new file mode 100644 index 0000000..d28f5ab --- /dev/null +++ b/libbrep/build.hxx @@ -0,0 +1,166 @@ +// file : libbrep/build.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef LIBBREP_BUILD_HXX +#define LIBBREP_BUILD_HXX + +#include <chrono> + +#include <odb/core.hxx> +#include <odb/section.hxx> + +#include <libbbot/manifest.hxx> + +#include <libbrep/types.hxx> +#include <libbrep/utility.hxx> + +#include <libbrep/common.hxx> // Must be included last (see assert). + +// Used by the data migration entries. +// +#define LIBBREP_BUILD_SCHEMA_VERSION_BASE 1 + +#pragma db model version(LIBBREP_BUILD_SCHEMA_VERSION_BASE, 1, open) + +// We have to keep these mappings at the global scope instead of inside +// the brep namespace because they need to be also effective in the +// bbot namespace from which we "borrow" types (and some of them use the mapped +// types). +// +#pragma db map type(bbot::result_status) as(std::string) \ + to(to_string (?)) \ + from(bbot::to_result_status (?)) + +namespace brep +{ + #pragma db value + struct build_id + { + package_id package; + string configuration; + + build_id () = default; + build_id (package_id p, string c) + : package (move (p)), configuration (move (c)) {} + }; + + inline bool + operator< (const build_id& x, const build_id& y) + { + return + x.package < y.package ? true : + y.package < x.package ? false : + x.configuration < y.configuration; + } + + // build_state + // + enum class build_state: std::uint8_t + { + untested, + testing, + tested + }; + + string + to_string (build_state); + + build_state + to_build_state (const string&); // May throw invalid_argument. + + inline ostream& + operator<< (ostream& os, build_state s) {return os << to_string (s);} + + #pragma db map type(build_state) as(string) \ + to(to_string (?)) \ + from(brep::to_build_state (?)) + + // result_status + // + using bbot::result_status; + + using optional_result_status = optional<result_status>; + + #pragma db map type(optional_result_status) as(optional_string) \ + to((?) ? bbot::to_string (*(?)) : brep::optional_string ()) \ + from((?) \ + ? bbot::to_result_status (*(?)) \ + : brep::optional_result_status ()) + + // operation_results + // + using bbot::operation_result; + #pragma db value(operation_result) definition + + using bbot::operation_results; + + #pragma db object pointer(shared_ptr) session + class build + { + public: + using timestamp_type = brep::timestamp; + + // Create the build object with the testing state, non-existent status, + // the timestamp set to now and the forced flag set to false. + // + build (string name, version, + string configuration, + string machine, string machine_summary); + + build_id id; + + string& package_name; // Tracks id.package.name. + upstream_version package_version; // Original of id.package.version. + string& configuration; // Tracks id.configuration. + + build_state state; + + // Time of the last state change (the creation time initially). + // + timestamp_type timestamp; + + // True if the package rebuild has been forced. + // + bool forced; + + // Present only if the state is 'tested'. + // + optional<result_status> status; + + // Present only if the state is 'testing' or 'tested'. + // + optional<string> machine; + optional<string> machine_summary; + + // Note that the logs are stored as std::string/TEXT which is Ok since + // they are UTF-8 and our database is UTF-8. + // + #pragma db section(results_section) + operation_results results; + + #pragma db load(lazy) update(always) + odb::section results_section; + + // Database mapping. + // + #pragma db member(id) id column("") + + #pragma db member(package_name) transient + #pragma db member(package_version) \ + set(this.package_version.init (this.id.package.version, (?))) + #pragma db member(configuration) transient + + #pragma db member(results) id_column("") value_column("") + + build (const build&) = delete; + build& operator= (const build&) = delete; + + private: + friend class odb::access; + build () + : package_name (id.package.name), configuration (id.configuration) {} + }; +} + +#endif // LIBBREP_BUILD_HXX diff --git a/libbrep/build.xml b/libbrep/build.xml new file mode 100644 index 0000000..8d9178a --- /dev/null +++ b/libbrep/build.xml @@ -0,0 +1,67 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" schema-name="build" version="1"> + <model version="1"> + <table name="build" kind="object"> + <column name="package_name" type="TEXT" null="false"/> + <column name="package_version_epoch" type="INTEGER" null="false"/> + <column name="package_version_canonical_upstream" type="TEXT" null="false"/> + <column name="package_version_canonical_release" type="TEXT" null="false" options="COLLATE "C""/> + <column name="package_version_revision" type="INTEGER" null="false"/> + <column name="configuration" type="TEXT" null="false"/> + <column name="package_version_upstream" type="TEXT" null="false"/> + <column name="package_version_release" type="TEXT" null="true"/> + <column name="state" type="TEXT" null="false"/> + <column name="timestamp" type="BIGINT" null="false"/> + <column name="forced" type="BOOLEAN" null="false"/> + <column name="status" type="TEXT" null="true"/> + <column name="machine" type="TEXT" null="true"/> + <column name="machine_summary" type="TEXT" null="true"/> + <primary-key> + <column name="package_name"/> + <column name="package_version_epoch"/> + <column name="package_version_canonical_upstream"/> + <column name="package_version_canonical_release"/> + <column name="package_version_revision"/> + <column name="configuration"/> + </primary-key> + </table> + <table name="build_results" kind="container"> + <column name="package_name" type="TEXT" null="false"/> + <column name="package_version_epoch" type="INTEGER" null="false"/> + <column name="package_version_canonical_upstream" type="TEXT" null="false"/> + <column name="package_version_canonical_release" type="TEXT" null="false" options="COLLATE "C""/> + <column name="package_version_revision" type="INTEGER" null="false"/> + <column name="configuration" type="TEXT" null="false"/> + <column name="index" type="BIGINT" null="false"/> + <column name="operation" type="TEXT" null="false"/> + <column name="status" type="TEXT" null="false"/> + <column name="log" type="TEXT" null="false"/> + <foreign-key name="object_id_fk" on-delete="CASCADE"> + <column name="package_name"/> + <column name="package_version_epoch"/> + <column name="package_version_canonical_upstream"/> + <column name="package_version_canonical_release"/> + <column name="package_version_revision"/> + <column name="configuration"/> + <references table="build"> + <column name="package_name"/> + <column name="package_version_epoch"/> + <column name="package_version_canonical_upstream"/> + <column name="package_version_canonical_release"/> + <column name="package_version_revision"/> + <column name="configuration"/> + </references> + </foreign-key> + <index name="build_results_object_id_i"> + <column name="package_name"/> + <column name="package_version_epoch"/> + <column name="package_version_canonical_upstream"/> + <column name="package_version_canonical_release"/> + <column name="package_version_revision"/> + <column name="configuration"/> + </index> + <index name="build_results_index_i"> + <column name="index"/> + </index> + </table> + </model> +</changelog> diff --git a/libbrep/buildfile b/libbrep/buildfile new file mode 100644 index 0000000..b4005ab --- /dev/null +++ b/libbrep/buildfile @@ -0,0 +1,50 @@ +# file : libbrep/buildfile +# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file + +define sql: file +sql{*}: extension = sql +sql{*}: install = data/ + +import int_libs = libodb%lib{odb} +import int_libs += libodb-pgsql%lib{odb-pgsql} +import int_libs += libbutl%lib{butl} +import int_libs += libbpkg%lib{bpkg} +import int_libs += libbbot%lib{bbot} + +lib{brep}: \ +{hxx cxx}{ build } \ +{file }{ build.xml } \ +{hxx ixx cxx}{ build-odb } \ +{hxx cxx}{ common } \ +{hxx ixx cxx}{ common-odb } \ +{hxx cxx}{ package } \ +{file }{ package.xml } \ +{hxx ixx cxx}{ package-odb } \ +{hxx }{ package-extra } \ +{hxx cxx}{ package-traits } \ +{hxx cxx}{ database-lock } \ +{hxx }{ types } \ +{hxx }{ utility } \ +{hxx }{ version } \ +{hxx }{ wrapper-traits } \ + $int_libs \ +sql{build package package-extra} + +hxx{version}: in{version} $src_root/file{manifest} +hxx{version}: dist = true + +# For pre-releases use the complete version to make sure they cannot be used +# in place of another pre-release or the final version. +# +if $version.pre_release + lib{brep}: bin.lib.version = @"-$version.project_id" +else + lib{brep}: bin.lib.version = @"-$version.major.$version.minor" + +lib{brep}: cxx.export.poptions = "-I$out_root" "-I$src_root" +lib{brep}: cxx.export.libs = $int_libs + +# Install into the libbrep/ subdirectory of, say, /usr/include/. +# +install.include = $install.include/libbrep/ diff --git a/libbrep/common.cxx b/libbrep/common.cxx new file mode 100644 index 0000000..39fd0c7 --- /dev/null +++ b/libbrep/common.cxx @@ -0,0 +1,10 @@ +// file : libbrep/common.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include <libbrep/common.hxx> + +namespace brep +{ + const version wildcard_version (0, "0", nullopt, 0); +} diff --git a/libbrep/common.hxx b/libbrep/common.hxx new file mode 100644 index 0000000..bf60491 --- /dev/null +++ b/libbrep/common.hxx @@ -0,0 +1,341 @@ +// file : libbrep/common.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef LIBBREP_COMMON_HXX +#define LIBBREP_COMMON_HXX + +#include <ratio> +#include <chrono> +#include <type_traits> // static_assert + +#include <libbrep/types.hxx> +#include <libbrep/utility.hxx> + +// The uint16_t value range is not fully covered by SMALLINT PostgreSQL type +// to which uint16_t is mapped by default. +// +#pragma db value(uint16_t) type("INTEGER") + +namespace brep +{ + // Use an image type to map bpkg::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; + string canonical_release; + uint16_t revision; + string upstream; + optional<string> release; + }; +} + +#include <bpkg/manifest> + +namespace brep +{ + using optional_version = optional<bpkg::version>; + using _optional_version = optional<_version>; +} + +// Prevent assert() macro expansion in get/set expressions. This should +// appear after all #include directives since the assert() macro is +// redefined in each <assert.h> inclusion. +// +#ifdef ODB_COMPILER +# undef assert +# define assert assert +void assert (int); +#endif + +// We have to keep these mappings at the global scope instead of inside +// the brep namespace because they need to be also effective in the +// bpkg namespace from which we "borrow" types (and some of them use version). +// +#pragma db map type(bpkg::version) as(brep::_version) \ + to(brep::_version{(?).epoch, \ + (?).canonical_upstream, \ + (?).canonical_release, \ + (?).revision, \ + (?).upstream, \ + (?).release}) \ + from(bpkg::version ((?).epoch, \ + std::move ((?).upstream), \ + std::move ((?).release), \ + (?).revision)) + +#pragma db map type(brep::optional_version) as(brep::_optional_version) \ + to((?) \ + ? brep::_version{(?)->epoch, \ + (?)->canonical_upstream, \ + (?)->canonical_release, \ + (?)->revision, \ + (?)->upstream, \ + (?)->release} \ + : brep::_optional_version ()) \ + from((?) \ + ? bpkg::version ((?)->epoch, \ + std::move ((?)->upstream), \ + std::move ((?)->release), \ + (?)->revision) \ + : brep::optional_version ()) + +namespace brep +{ + // path + // + #pragma db map type(path) as(string) to((?).string ()) from(brep::path (?)) + + using optional_path = optional<path>; + using optional_string = optional<string>; + + #pragma db map type(optional_path) as(brep::optional_string) \ + to((?) ? (?)->string () : brep::optional_string ()) \ + from((?) ? brep::path (*(?)) : brep::optional_path ()) + + #pragma db map type(dir_path) as(string) \ + to((?).string ()) from(brep::dir_path (?)) + + // Ensure that timestamp can be represented in nonoseconds without loss of + // accuracy, so the following ODB mapping is adequate. + // + static_assert( + std::ratio_greater_equal<timestamp::period, + std::chrono::nanoseconds::period>::value, + "The following timestamp ODB mapping is invalid"); + + // As it pointed out in butl/timestamp we will overflow in year 2262, but + // by that time some larger basic type will be available for mapping. + // + #pragma db map type(timestamp) as(uint64_t) \ + to(std::chrono::duration_cast<std::chrono::nanoseconds> ( \ + (?).time_since_epoch ()).count ()) \ + from(brep::timestamp ( \ + std::chrono::duration_cast<brep::timestamp::duration> ( \ + std::chrono::nanoseconds (?)))) + + // version + // + using bpkg::version; + + #pragma db value + struct canonical_version + { + uint16_t epoch; + string canonical_upstream; + string canonical_release; + uint16_t revision; + + bool + empty () const noexcept + { + // Note that an empty canonical_upstream doesn't denote an empty + // canonical_version. Remeber, that canonical_upstream doesn't include + // rightmost digit-only zero components? So non-empty version("0") has + // an empty canonical_upstream. + // + return epoch == 0 && canonical_upstream.empty () && + canonical_release.empty () && revision == 0; + } + + // Change collation to ensure the proper comparison of the "absent" release + // with a specified one. + // + // The default collation for UTF8-encoded TEXT columns in PostgreSQL is + // UCA-compliant. This makes the statement 'a' < '~' to be false, which + // in turn makes the statement 2.1.alpha < 2.1 to be false as well. + // + // Unicode Collation Algorithm (UCA): http://unicode.org/reports/tr10/ + // + #pragma db member(canonical_release) options("COLLATE \"C\"") + }; + + #pragma db value transient + struct upstream_version: version + { + #pragma db member(upstream_) virtual(string) \ + get(this.upstream) \ + set(this = brep::version (0, std::move (?), std::string (), 0)) + + #pragma db member(release_) virtual(optional_string) \ + get(this.release) \ + set(this = brep::version ( \ + 0, std::move (this.upstream), 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, uv.release, cv.revision); + assert (cv.canonical_upstream == canonical_upstream && + cv.canonical_release == canonical_release); + } + }; + + // Wildcard version. Satisfies any dependency constraint and is represented + // as 0+0 (which is also the "stub version"; since a real version is always + // greater than the stub version, we reuse it to signify a special case). + // + extern const version wildcard_version; + + #pragma db value + struct package_id + { + string name; + canonical_version version; + + package_id () = default; + package_id (string n, const brep::version& v) + : name (move (n)), + version { + v.epoch, v.canonical_upstream, v.canonical_release, v.revision} + { + } + }; + + // Version comparison operators. + // + // They allow comparing objects that have epoch, canonical_upstream, + // canonical_release, and revision data members. The idea is that this + // works for both query members of types version and canonical_version + // as well as for comparing canonical_version to version. + // + template <typename T1, typename T2> + inline auto + compare_version_eq (const T1& x, const T2& y, bool revision) + -> decltype (x.epoch == y.epoch) + { + // Since we don't quite know what T1 and T2 are (and where the resulting + // expression will run), let's not push our luck with something like + // (!revision || x.revision == y.revision). + // + auto r (x.epoch == y.epoch && + x.canonical_upstream == y.canonical_upstream && + x.canonical_release == y.canonical_release); + + return revision + ? r && x.revision == y.revision + : r; + } + + template <typename T1, typename T2> + inline auto + compare_version_ne (const T1& x, const T2& y, bool revision) + -> decltype (x.epoch == y.epoch) + { + auto r (x.epoch != y.epoch || + x.canonical_upstream != y.canonical_upstream || + x.canonical_release != y.canonical_release); + + return revision + ? r || x.revision != y.revision + : r; + } + + template <typename T1, typename T2> + inline auto + compare_version_lt (const T1& x, const T2& y, bool revision) + -> decltype (x.epoch == y.epoch) + { + auto r ( + 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.canonical_release < y.canonical_release)); + + return revision + ? r || + (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream && + x.canonical_release == y.canonical_release && x.revision < y.revision) + : r; + } + + template <typename T1, typename T2> + inline auto + compare_version_le (const T1& x, const T2& y, bool revision) + -> decltype (x.epoch == y.epoch) + { + auto r ( + x.epoch < y.epoch || + (x.epoch == y.epoch && x.canonical_upstream < y.canonical_upstream)); + + return revision + ? r || + (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream && + x.canonical_release < y.canonical_release) || + (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream && + x.canonical_release == y.canonical_release && x.revision <= y.revision) + : r || + (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream && + x.canonical_release <= y.canonical_release); + } + + template <typename T1, typename T2> + inline auto + compare_version_gt (const T1& x, const T2& y, bool revision) + -> decltype (x.epoch == y.epoch) + { + auto r ( + 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.canonical_release > y.canonical_release)); + + return revision + ? r || + (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream && + x.canonical_release == y.canonical_release && x.revision > y.revision) + : r; + } + + template <typename T1, typename T2> + inline auto + compare_version_ge (const T1& x, const T2& y, bool revision) + -> decltype (x.epoch == y.epoch) + { + auto r ( + x.epoch > y.epoch || + (x.epoch == y.epoch && x.canonical_upstream > y.canonical_upstream)); + + return revision + ? r || + (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream && + x.canonical_release > y.canonical_release) || + (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream && + x.canonical_release == y.canonical_release && x.revision >= y.revision) + : r || + (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream && + x.canonical_release >= y.canonical_release); + } + + template <typename T> + 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.canonical_release + "DESC," + + x.revision + "DESC"; + } + + inline bool + operator< (const package_id& x, const package_id& y) + { + if (int r = x.name.compare (y.name)) + return r < 0; + + return compare_version_lt (x.version, y.version, true); + } +} + +#endif // LIBBREP_COMMON_HXX diff --git a/libbrep/database-lock.cxx b/libbrep/database-lock.cxx new file mode 100644 index 0000000..c69c240 --- /dev/null +++ b/libbrep/database-lock.cxx @@ -0,0 +1,44 @@ +// file : libbrep/database-lock.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include <libbrep/database-lock.hxx> + +#include <odb/pgsql/database.hxx> +#include <odb/pgsql/exceptions.hxx> +#include <odb/pgsql/transaction.hxx> + +namespace brep +{ + using namespace odb::pgsql; + + database_lock:: + database_lock (database& db) + { + // Before locking the table make sure it exists. + // + { + transaction t (db.begin ()); + db.execute ("CREATE TABLE IF NOT EXISTS database_mutex ()"); + t.commit (); + } + + connection_ = db.connection (); + + // Don't make current. Will be rolled back in destructor. + // + transaction_.reset (new transaction (connection_->begin (), false)); + + try + { + connection_->execute ("LOCK TABLE database_mutex NOWAIT"); + } + catch (const database_exception& e) + { + if (e.sqlstate () == "55P03") // The table is already locked. + throw database_locked (); + + throw; + } + } +} diff --git a/libbrep/database-lock.hxx b/libbrep/database-lock.hxx new file mode 100644 index 0000000..60d57a4 --- /dev/null +++ b/libbrep/database-lock.hxx @@ -0,0 +1,43 @@ +// file : libbrep/database-lock.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef LIBBREP_DATABASE_LOCK_HXX +#define LIBBREP_DATABASE_LOCK_HXX + +#include <odb/pgsql/forward.hxx> // database, transaction +#include <odb/pgsql/connection.hxx> + +#include <libbrep/types.hxx> +#include <libbrep/utility.hxx> + +namespace brep +{ + struct database_locked: std::exception + { + virtual char const* + what () const throw () {return "database locked";} + }; + + // Try to "lock" the PostgreSQL database in the constructor and release the + // lock in the destructor. Throw database_locked if the database is already + // locked by someone else. May also throw odb::pgsql::database_exception. + // + // This mechanism is used by the brep loader and schema migration tool to + // make sure they don't step on each others toes. + // + // Note: movable but not copyable. + // + class database_lock + { + public: + explicit + database_lock (odb::pgsql::database&); + + private: + odb::pgsql::connection_ptr connection_; + unique_ptr<odb::pgsql::transaction> transaction_; + }; +} + +#endif // LIBBREP_DATABASE_LOCK_HXX diff --git a/libbrep/odb.sh b/libbrep/odb.sh new file mode 100755 index 0000000..b794a2d --- /dev/null +++ b/libbrep/odb.sh @@ -0,0 +1,40 @@ +#! /usr/bin/env bash + +trap 'exit 1' ERR + +odb=odb +lib="\ +-I$HOME/work/odb/libodb-pgsql-default \ +-I$HOME/work/odb/libodb-pgsql \ +-I$HOME/work/odb/libodb-default \ +-I$HOME/work/odb/libodb" + +$odb $lib -d pgsql --std c++11 --generate-query \ + --odb-epilogue '#include <libbrep/wrapper-traits.hxx>' \ + --hxx-prologue '#include <libbrep/wrapper-traits.hxx>' \ + -DLIBODB_BUILD2 -DLIBODB_PGSQL_BUILD2 \ + -I .. -I ../../libbbot -I ../../libbpkg -I ../../libbutl \ + --hxx-suffix ".hxx" --include-with-brackets \ + --include-prefix libbrep --guard-prefix LIBBREP \ + common.hxx + +$odb $lib -d pgsql --std c++11 --generate-query --generate-schema \ + --schema-format sql --schema-format embedded --schema-name package \ + --odb-epilogue '#include <libbrep/wrapper-traits.hxx>' \ + --hxx-prologue '#include <libbrep/package-traits.hxx>' \ + --generate-prepared -DLIBODB_BUILD2 -DLIBODB_PGSQL_BUILD2 \ + -I .. -I ../../libbbot -I ../../libbpkg -I ../../libbutl \ + --hxx-suffix ".hxx" --include-with-brackets \ + --include-prefix libbrep --guard-prefix LIBBREP \ + package.hxx + +xxd -i <package-extra.sql >package-extra.hxx + +$odb $lib -d pgsql --std c++11 --generate-query --generate-schema \ + --schema-format sql --schema-format embedded --schema-name build \ + --odb-epilogue '#include <libbrep/wrapper-traits.hxx>' \ + --generate-prepared -DLIBODB_BUILD2 -DLIBODB_PGSQL_BUILD2 \ + -I .. -I ../../libbbot -I ../../libbpkg -I ../../libbutl \ + --hxx-suffix ".hxx" --include-with-brackets \ + --include-prefix libbrep --guard-prefix LIBBREP \ + build.hxx diff --git a/libbrep/package-extra.sql b/libbrep/package-extra.sql new file mode 100644 index 0000000..823c3af --- /dev/null +++ b/libbrep/package-extra.sql @@ -0,0 +1,130 @@ +-- This file should be parsable by the brep-migrate utility. To decrease the +-- parser complexity, the following restrictions are placed: +-- +-- * comments must start with -- at the beginning of the line (ignoring +-- leading spaces) +-- * only CREATE and DROP statements for FUNCTION and TYPE +-- * function bodies must be defined using $$-quoted strings +-- * strings other then function bodies must be quoted with ' or " +-- * statements must end with ";\n" +-- + +-- There is no need to drop to_tsvector() explicitly, as we can rely on "DROP +-- TYPE IF EXISTS weighted_text CASCADE" statement below, which will drop all +-- objects that depend on this type. Moreover this DROP FUNCTION statement will +-- fail for old versions of PostgreSQL (experienced for 9.2.14) with error: +-- type "weighted_text" does not exist. +-- +-- DROP FUNCTION IF EXISTS to_tsvector(IN document weighted_text); +-- +DROP FUNCTION IF EXISTS search_packages(IN query tsquery, INOUT name TEXT); +DROP FUNCTION IF EXISTS search_latest_packages(IN query tsquery); +DROP FUNCTION IF EXISTS latest_package(INOUT name TEXT); +DROP FUNCTION IF EXISTS latest_packages(); + +DROP TYPE IF EXISTS weighted_text CASCADE; +CREATE TYPE weighted_text AS (a TEXT, b TEXT, c TEXT, d TEXT); + +-- Return the latest versions of internal packages as a set of package rows. +-- +CREATE FUNCTION +latest_packages() +RETURNS SETOF package AS $$ + SELECT p1.* + FROM package p1 LEFT JOIN package p2 ON ( + p1.internal_repository IS NOT NULL AND p1.name = p2.name AND + p2.internal_repository IS NOT NULL AND + (p1.version_epoch < p2.version_epoch OR + p1.version_epoch = p2.version_epoch AND + (p1.version_canonical_upstream < p2.version_canonical_upstream OR + p1.version_canonical_upstream = p2.version_canonical_upstream AND + (p1.version_canonical_release < p2.version_canonical_release OR + p1.version_canonical_release = p2.version_canonical_release AND + p1.version_revision < p2.version_revision)))) + WHERE + p1.internal_repository IS NOT NULL AND p2.name IS NULL; +$$ LANGUAGE SQL STABLE; + +-- Find the latest version of an internal package having the specified name. +-- Return a single row containing the package id, empty row set if the package +-- not found. +-- +CREATE FUNCTION +latest_package(INOUT name TEXT, + OUT version_epoch INTEGER, + OUT version_canonical_upstream TEXT, + OUT version_canonical_release TEXT, + OUT version_revision INTEGER) +RETURNS SETOF record AS $$ + SELECT name, version_epoch, version_canonical_upstream, + version_canonical_release, version_revision + FROM latest_packages() + WHERE name = latest_package.name; +$$ LANGUAGE SQL STABLE; + +-- Search for the latest version of an internal packages matching the specified +-- search query. Return a set of rows containing the package id and search +-- rank. If query is NULL, then match all packages and return 0 rank for +-- all rows. +-- +CREATE FUNCTION +search_latest_packages(IN query tsquery, + OUT name TEXT, + OUT version_epoch INTEGER, + OUT version_canonical_upstream TEXT, + OUT version_canonical_release TEXT, + OUT version_revision INTEGER, + OUT rank real) +RETURNS SETOF record AS $$ + SELECT name, version_epoch, version_canonical_upstream, + version_canonical_release, version_revision, + CASE + WHEN query IS NULL THEN 0 +-- Weight mapping: D C B A + ELSE ts_rank_cd('{0.05, 0.2, 0.9, 1.0}', search_index, query) + END AS rank + FROM latest_packages() + WHERE query IS NULL OR search_index @@ query; +$$ LANGUAGE SQL STABLE; + +-- Search for packages matching the search query and having the specified name. +-- Return a set of rows containing the package id and search rank. If query +-- is NULL, then match all packages and return 0 rank for all rows. +-- +CREATE FUNCTION +search_packages(IN query tsquery, + INOUT name TEXT, + OUT version_epoch INTEGER, + OUT version_canonical_upstream TEXT, + OUT version_canonical_release TEXT, + OUT version_revision INTEGER, + OUT rank real) +RETURNS SETOF record AS $$ + SELECT name, version_epoch, version_canonical_upstream, + version_canonical_release, version_revision, + CASE + WHEN query IS NULL THEN 0 +-- Weight mapping: D C B A + ELSE ts_rank_cd('{0.05, 0.2, 0.9, 1.0}', search_index, query) + END AS rank + FROM package + WHERE + internal_repository IS NOT NULL AND name = search_packages.name AND + (query IS NULL OR search_index @@ query); +$$ LANGUAGE SQL STABLE; + +-- Parse weighted_text to tsvector. +-- +CREATE FUNCTION +to_tsvector(IN document weighted_text) +RETURNS tsvector AS $$ + SELECT + CASE + WHEN document IS NULL THEN NULL + ELSE + setweight(to_tsvector(document.a), 'A') || + setweight(to_tsvector(document.b), 'B') || + setweight(to_tsvector(document.c), 'C') || + setweight(to_tsvector(document.d), 'D') + END +$$ LANGUAGE SQL IMMUTABLE; diff --git a/libbrep/package-traits.cxx b/libbrep/package-traits.cxx new file mode 100644 index 0000000..a03cbac --- /dev/null +++ b/libbrep/package-traits.cxx @@ -0,0 +1,70 @@ +// file : libbrep/package-traits.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include <libbrep/package-traits.hxx> + +#include <string> +#include <ostream> +#include <sstream> +#include <cstring> // memcpy + +#include <odb/pgsql/traits.hxx> + +using namespace std; + +namespace odb +{ + namespace pgsql + { + static inline void + to_pg_string (ostream& os, const string& s) + { + os << '"'; + + for (auto c: s) + { + if (c == '\\' || c == '"') + os << '\\'; + + os << c; + } + + os << '"'; + } + + // Convert C++ weighted_text struct to PostgreSQL weighted_text + // composite type. + // + void value_traits<brep::weighted_text, id_string>:: + set_image (details::buffer& b, + size_t& n, + bool& is_null, + const value_type& v) + { + is_null = v.a.empty () && v.b.empty () && v.c.empty () && v.d.empty (); + + if (!is_null) + { + ostringstream o; + o << "("; + to_pg_string (o, v.a); + o << ","; + to_pg_string (o, v.b); + o << ","; + to_pg_string (o, v.c); + o << ","; + to_pg_string (o, v.d); + o << ")"; + + const string& s (o.str ()); + n = s.size (); + + if (n > b.capacity ()) + b.capacity (n); + + memcpy (b.data (), s.c_str (), n); + } + } + } +} diff --git a/libbrep/package-traits.hxx b/libbrep/package-traits.hxx new file mode 100644 index 0000000..b626ea8 --- /dev/null +++ b/libbrep/package-traits.hxx @@ -0,0 +1,38 @@ +// file : brep/package-traits -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef BREP_PACKAGE_TRAITS +#define BREP_PACKAGE_TRAITS + +#include <cstddef> // size_t + +#include <odb/pgsql/traits.hxx> + +#include <libbrep/package.hxx> // weighted_text + +namespace odb +{ + namespace pgsql + { + template <> + class value_traits<brep::weighted_text, id_string> + { + public: + typedef brep::weighted_text value_type; + typedef value_type query_type; + typedef details::buffer image_type; + + static void + set_value (value_type&, const details::buffer&, std::size_t, bool) {} + + static void + set_image (details::buffer&, + std::size_t& n, + bool& is_null, + const value_type&); + }; + } +} + +#endif // BREP_PACKAGE_TRAITS diff --git a/libbrep/package.cxx b/libbrep/package.cxx new file mode 100644 index 0000000..20be387 --- /dev/null +++ b/libbrep/package.cxx @@ -0,0 +1,169 @@ +// file : libbrep/package.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include <libbrep/package.hxx> + +#include <odb/database.hxx> + +#include <libbrep/package-odb.hxx> + +using namespace std; +using namespace odb::core; + +namespace brep +{ + // dependency + // + string dependency:: + name () const + { + return package.object_id ().name; + } + + ostream& + operator<< (ostream& o, const dependency& d) + { + o << d.name (); + + if (d.constraint) + o << ' ' << *d.constraint; + + return o; + } + + bool + operator== (const dependency& x, const dependency& y) + { + return x.name () == y.name () && x.constraint == y.constraint; + } + + bool + operator!= (const dependency& x, const dependency& y) + { + return !(x == y); + } + + // package + // + package:: + package (string nm, + version_type vr, + priority_type pr, + string sm, + license_alternatives_type la, + strings tg, + optional<string> ds, + string ch, + url_type ur, + optional<url_type> pu, + email_type em, + optional<email_type> pe, + optional<email_type> be, + dependencies_type dp, + requirements_type rq, + optional<path> lc, + optional<string> sh, + shared_ptr<repository_type> rp) + : id (move (nm), vr), + version (move (vr)), + priority (move (pr)), + summary (move (sm)), + license_alternatives (move (la)), + tags (move (tg)), + description (move (ds)), + changes (move (ch)), + url (move (ur)), + package_url (move (pu)), + email (move (em)), + package_email (move (pe)), + build_email (move (be)), + dependencies (move (dp)), + requirements (move (rq)), + internal_repository (move (rp)), + location (move (lc)), + sha256sum (move (sh)) + { + assert (internal_repository->internal); + } + + package:: + package (string nm, + version_type vr, + shared_ptr<repository_type> rp) + : id (move (nm), vr), + version (move (vr)) + { + assert (!rp->internal); + other_repositories.emplace_back (move (rp)); + } + + weighted_text package:: + search_text () const + { + if (!internal ()) + return weighted_text (); + + // Derive keywords from the basic package information: name, + // version. + // + //@@ What about 'stable' from cppget.org/stable? Add path of + // the repository to keywords? Or is it too "polluting" and + // we will handle it in some other way (e.g., by allowing + // the user to specify repo location in the drop-down box)? + // Probably drop-box would be better as also tells what are + // the available internal repositories. + // + string k (id.name + " " + version.string () + " " + version.string (true)); + + // Add tags to keywords. + // + for (const auto& t: tags) + k += " " + t; + + // Add licenses to keywords. + // + for (const auto& la: license_alternatives) + { + for (const auto& l: la) + { + k += " " + l; + + // If license is say LGPLv2 then LGPL is also a keyword. + // + size_t n (l.size ()); + if (n > 2 && l[n - 2] == 'v' && l[n - 1] >= '0' && l[n - 1] <= '9') + k += " " + string (l, 0, n - 2); + } + } + + return {move (k), summary, description ? *description : "", changes}; + } + + // repository + // + repository:: + repository (repository_location l, + string d, + repository_location h, + optional<certificate_type> c, + uint16_t r) + : name (l.canonical_name ()), + location (move (l)), + display_name (move (d)), + priority (r), + cache_location (move (h)), + certificate (move (c)), + internal (true) + { + } + + repository:: + repository (repository_location l) + : name (l.canonical_name ()), + location (move (l)), + priority (0), + internal (false) + { + } +} diff --git a/libbrep/package.hxx b/libbrep/package.hxx new file mode 100644 index 0000000..8164639 --- /dev/null +++ b/libbrep/package.hxx @@ -0,0 +1,500 @@ +// file : libbrep/package.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef LIBBREP_PACKAGE_HXX +#define LIBBREP_PACKAGE_HXX + +#include <map> +#include <chrono> + +#include <odb/core.hxx> +#include <odb/nested-container.hxx> + +#include <libbrep/types.hxx> +#include <libbrep/utility.hxx> + +#include <libbrep/common.hxx> // Must be included last (see assert). + +// Used by the data migration entries. +// +#define LIBBREP_PACKAGE_SCHEMA_VERSION_BASE 4 + +#pragma db model version(LIBBREP_PACKAGE_SCHEMA_VERSION_BASE, 4, open) + +namespace brep +{ + // @@ Might make sense to put some heavy members (e.g., description, + // containers) into a separate section. + // + // @@ Not sure there is a benefit in making tags a full-blown container + // (i.e., a separate table). Maybe provide a mapping of vector<string> + // to TEXT as a comma-separated list. + // + + // Forward declarations. + // + class repository; + class package; + + // priority + // + using bpkg::priority; + + #pragma db value(priority) definition + #pragma db member(priority::value) column("") + + // url + // + using bpkg::url; + + #pragma db value(url) definition + #pragma db member(url::value) virtual(string) before access(this) column("") + + // email + // + using bpkg::email; + + #pragma db value(email) definition + #pragma db member(email::value) virtual(string) before access(this) column("") + + // licenses + // + using bpkg::licenses; + using license_alternatives = vector<licenses>; + + #pragma db value(licenses) definition + + // dependencies + // + using bpkg::dependency_constraint; + + #pragma db value(dependency_constraint) definition + + // Notes: + // + // 1. Will the package be always resolvable? What if it is in + // another repository (i.e., a "chained" third-party repo). + // The question is then whether we will load such "third- + // party packages" (i.e., packages that are not in our + // repository). If the answer is yes, then we can have + // a pointer here. If the answer is no, then we can't. + // Also, if the answer is yes, we probably don't need to + // load as much information as for "our own" packages. We + // also shouldn't be showing them in search results, etc. + // I think all we need is to know which repository this + // package comes from so that we can tell the user. How are + // we going to capture this? Poly hierarchy of packages? + // + // 2. I believe we don't need to use a weak pointer here since + // there should be no package dependency cycles (and therefore + // ownership cycles). + // + // 3. Actually there can be dependency cycle as dependency referes not to + // just a package but a specific version, so for the same pair of + // packages dependency for different versions can have an opposite + // directions. The possible solution is instead of a package we point + // to the earliest version that satisfies the constraint. But this + // approach requires to ensure no cycles exist before instantiating + // package objects which in presense of "foreign" packages can be + // tricky. Can stick to just a package name until get some clarity on + // "foreign" package resolution. + // + // 4. As we left just the package class the dependency resolution come to + // finding the best version matching package object. The question is + // if to resolve dependencies on the loading phase or in the WEB interface + // when required. The arguments in favour of doing that during loading + // phase are: + // + // - WEB interface get offloaded from a possibly expensive queries + // which otherwise have to be executed multiple times for the same + // dependency no matter the result would be the same. + // + // - No need to complicate persisted object model with repository + // relations otherwise required just for dependency resolution. + // + + #pragma db value + struct dependency + { + using package_type = brep::package; + + lazy_shared_ptr<package_type> package; + optional<dependency_constraint> constraint; + + // Prerequisite package name. + // + string + name () const; + + // Database mapping. + // + #pragma db member(package) column("") not_null + #pragma db member(constraint) column("") + }; + + ostream& + operator<< (ostream&, const dependency&); + + bool + operator== (const dependency&, const dependency&); + + bool + operator!= (const dependency&, const dependency&); + + #pragma db value + class dependency_alternatives: public vector<dependency> + { + public: + bool conditional; + bool buildtime; + string comment; + + dependency_alternatives () = default; + dependency_alternatives (bool d, bool b, string c) + : conditional (d), buildtime (b), comment (move (c)) {} + }; + + using dependencies = vector<dependency_alternatives>; + + // requirements + // + using bpkg::requirement_alternatives; + using requirements = vector<requirement_alternatives>; + + #pragma db value(requirement_alternatives) definition + + // repository_location + // + using bpkg::repository_location; + + #pragma db map type(repository_location) as(string) \ + to((?).string ()) from(brep::repository_location (?)) + + #pragma db value + class certificate + { + public: + string fingerprint; // SHA256 fingerprint. + string name; // CN component of Subject. + string organization; // O component of Subject. + string email; // email: in Subject Alternative Name. + string pem; // PEM representation. + }; + + #pragma db object pointer(shared_ptr) session + class repository + { + public: + using email_type = brep::email; + using certificate_type = brep::certificate; + + // Create internal repository. + // + repository (repository_location, + string display_name, + repository_location cache_location, + optional<certificate_type>, + uint16_t priority); + + // Create external repository. + // + explicit + repository (repository_location); + + string name; // Object id (canonical name). + repository_location location; + string display_name; + + // The order in the internal repositories configuration file, starting from + // 1. 0 for external repositories. + // + uint16_t priority; + + optional<string> url; + + // Present only for internal repositories. + // + optional<email_type> email; + optional<string> summary; + optional<string> description; + + // Location of the repository local cache. Non empty for internal + // repositories and external ones with a filesystem path location. + // + repository_location cache_location; + + // Present only for internal signed repositories. + // + optional<certificate_type> certificate; + + // Initialized with timestamp_nonexistent by default. + // + timestamp packages_timestamp; + + // Initialized with timestamp_nonexistent by default. + // + timestamp repositories_timestamp; + + bool internal; + vector<lazy_weak_ptr<repository>> complements; + vector<lazy_weak_ptr<repository>> prerequisites; + + // Database mapping. + // + #pragma db member(name) id + + #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 + + #pragma db member(prerequisites) id_column("repository") \ + value_column("prerequisite") value_not_null + + private: + friend class odb::access; + repository () = default; + }; + + // The 'to' expression calls the PostgreSQL to_tsvector(weighted_text) + // function overload (package-extra.sql). Since we are only interested + // in "write-only" members of this type, make the 'from' expression + // always return empty string (we still have to work the placeholder + // in to keep overprotective ODB happy). + // + #pragma db map type("tsvector") as("TEXT") \ + to("to_tsvector((?)::weighted_text)") from("COALESCE('',(?))") + + // C++ type for weighted PostgreSQL tsvector. + // + #pragma db value type("tsvector") + struct weighted_text + { + string a; + string b; + string c; + string d; + }; + + #pragma db object pointer(shared_ptr) session + class package + { + public: + using repository_type = brep::repository; + using version_type = brep::version; + using priority_type = brep::priority; + using license_alternatives_type = brep::license_alternatives; + using url_type = brep::url; + using email_type = brep::email; + using dependencies_type = brep::dependencies; + using requirements_type = brep::requirements; + + // Create internal package object. + // + package (string name, + version_type, + priority_type, + string summary, + license_alternatives_type, + strings tags, + optional<string> description, + string changes, + url_type, + optional<url_type> package_url, + email_type, + optional<email_type> package_email, + optional<email_type> build_email, + dependencies_type, + requirements_type, + optional<path> location, + optional<string> sha256sum, + shared_ptr<repository_type>); + + // Create external package object. + // + // External repository packages can appear on the WEB interface only in + // dependency list in the form of a link to the corresponding WEB page. + // The only package information required to compose such a link is the + // package name, version, and repository location. + // + package (string name, version_type, shared_ptr<repository_type>); + + bool + internal () const noexcept {return internal_repository != nullptr;} + + // Manifest data. + // + package_id id; + upstream_version version; + priority_type priority; + string summary; + license_alternatives_type license_alternatives; + strings tags; + optional<string> description; + string changes; + url_type url; + optional<url_type> package_url; + email_type email; + optional<email_type> package_email; + optional<email_type> build_email; + dependencies_type dependencies; + requirements_type requirements; + lazy_shared_ptr<repository_type> internal_repository; + + // Path to the package file. Present only for internal packages. + // + optional<path> location; + + // Present only for internal packages. + // + optional<string> sha256sum; + + vector<lazy_shared_ptr<repository_type>> other_repositories; + + // Database mapping. + // + #pragma db member(id) id column("") + #pragma db member(version) set(this.version.init (this.id.version, (?))) + + // license + // + using _license_key = odb::nested_key<licenses>; + using _licenses_type = std::map<_license_key, string>; + + #pragma db value(_license_key) + #pragma db member(_license_key::outer) column("alternative_index") + #pragma db member(_license_key::inner) column("index") + + #pragma db member(license_alternatives) id_column("") value_column("") + #pragma db member(licenses) \ + virtual(_licenses_type) \ + after(license_alternatives) \ + get(odb::nested_get (this.license_alternatives)) \ + set(odb::nested_set (this.license_alternatives, std::move (?))) \ + id_column("") key_column("") value_column("license") + + // tags + // + #pragma db member(tags) id_column("") value_column("tag") + + // dependencies + // + using _dependency_key = odb::nested_key<dependency_alternatives>; + using _dependency_alternatives_type = + std::map<_dependency_key, dependency>; + + #pragma db value(_dependency_key) + #pragma db member(_dependency_key::outer) column("dependency_index") + #pragma db member(_dependency_key::inner) column("index") + + #pragma db member(dependencies) id_column("") value_column("") + #pragma db member(dependency_alternatives) \ + virtual(_dependency_alternatives_type) \ + after(dependencies) \ + get(odb::nested_get (this.dependencies)) \ + set(odb::nested_set (this.dependencies, std::move (?))) \ + id_column("") key_column("") value_column("dep_") + + // requirements + // + using _requirement_key = odb::nested_key<requirement_alternatives>; + using _requirement_alternatives_type = + std::map<_requirement_key, string>; + + #pragma db value(_requirement_key) + #pragma db member(_requirement_key::outer) column("requirement_index") + #pragma db member(_requirement_key::inner) column("index") + + #pragma db member(requirements) id_column("") value_column("") + #pragma db member(requirement_alternatives) \ + virtual(_requirement_alternatives_type) \ + after(requirements) \ + get(odb::nested_get (this.requirements)) \ + set(odb::nested_set (this.requirements, std::move (?))) \ + id_column("") key_column("") value_column("id") + + // other_repositories + // + #pragma db member(other_repositories) \ + id_column("") value_column("repository") value_not_null + + // search_index + // + #pragma db member(search_index) virtual(weighted_text) null \ + access(search_text) + + #pragma db index method("GIN") member(search_index) + + private: + friend class odb::access; + package () = default; + + // Save keywords, summary, description, and changes to weighted_text + // a, b, c, d members, respectively. So a word found in keywords will + // have a higher weight than if it's found in the summary. + // + weighted_text + search_text () const; + + // Noop as search_index is a write-only member. + // + void + search_text (const weighted_text&) {} + }; + + // Package search query matching rank. + // + #pragma db view query("/*CALL*/ SELECT * FROM search_latest_packages(?)") + struct latest_package_search_rank + { + package_id id; + double rank; + }; + + #pragma db view \ + query("/*CALL*/ SELECT count(*) FROM search_latest_packages(?)") + struct latest_package_count + { + size_t result; + + operator size_t () const {return result;} + }; + + #pragma db view query("/*CALL*/ SELECT * FROM search_packages(?)") + struct package_search_rank + { + package_id id; + double rank; + }; + + #pragma db view query("/*CALL*/ SELECT count(*) FROM search_packages(?)") + struct package_count + { + size_t result; + + operator size_t () const {return result;} + }; + + #pragma db view query("/*CALL*/ SELECT * FROM latest_package(?)") + struct latest_package + { + package_id id; + }; + + #pragma db view object(package) + struct package_version + { + package_id id; + upstream_version version; + + // Database mapping. + // + #pragma db member(id) column("") + #pragma db member(version) set(this.version.init (this.id.version, (?))) + }; +} + +#endif // LIBBREP_PACKAGE_HXX diff --git a/libbrep/package.xml b/libbrep/package.xml new file mode 100644 index 0000000..657c2fc --- /dev/null +++ b/libbrep/package.xml @@ -0,0 +1,417 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" schema-name="package" version="1"> + <model version="4"> + <table name="repository" kind="object"> + <column name="name" type="TEXT" null="false"/> + <column name="location" type="TEXT" null="false"/> + <column name="display_name" type="TEXT" null="false"/> + <column name="priority" type="INTEGER" null="false"/> + <column name="url" type="TEXT" null="true"/> + <column name="email" type="TEXT" null="true"/> + <column name="email_comment" type="TEXT" null="true"/> + <column name="summary" type="TEXT" null="true"/> + <column name="description" type="TEXT" null="true"/> + <column name="cache_location" type="TEXT" null="false"/> + <column name="certificate_fingerprint" type="TEXT" null="true"/> + <column name="certificate_name" type="TEXT" null="true"/> + <column name="certificate_organization" type="TEXT" null="true"/> + <column name="certificate_email" type="TEXT" null="true"/> + <column name="certificate_pem" type="TEXT" null="true"/> + <column name="packages_timestamp" type="BIGINT" null="false"/> + <column name="repositories_timestamp" type="BIGINT" null="false"/> + <column name="internal" type="BOOLEAN" null="false"/> + <primary-key> + <column name="name"/> + </primary-key> + </table> + <table name="repository_complements" kind="container"> + <column name="repository" type="TEXT" null="false"/> + <column name="index" type="BIGINT" null="false"/> + <column name="complement" type="TEXT" null="false"/> + <foreign-key name="repository_fk" on-delete="CASCADE"> + <column name="repository"/> + <references table="repository"> + <column name="name"/> + </references> + </foreign-key> + <index name="repository_complements_repository_i"> + <column name="repository"/> + </index> + <index name="repository_complements_index_i"> + <column name="index"/> + </index> + <foreign-key name="complement_fk" deferrable="DEFERRED"> + <column name="complement"/> + <references table="repository"> + <column name="name"/> + </references> + </foreign-key> + </table> + <table name="repository_prerequisites" kind="container"> + <column name="repository" type="TEXT" null="false"/> + <column name="index" type="BIGINT" null="false"/> + <column name="prerequisite" type="TEXT" null="false"/> + <foreign-key name="repository_fk" on-delete="CASCADE"> + <column name="repository"/> + <references table="repository"> + <column name="name"/> + </references> + </foreign-key> + <index name="repository_prerequisites_repository_i"> + <column name="repository"/> + </index> + <index name="repository_prerequisites_index_i"> + <column name="index"/> + </index> + <foreign-key name="prerequisite_fk" deferrable="DEFERRED"> + <column name="prerequisite"/> + <references table="repository"> + <column name="name"/> + </references> + </foreign-key> + </table> + <table name="package" kind="object"> + <column name="name" type="TEXT" null="false"/> + <column name="version_epoch" type="INTEGER" null="false"/> + <column name="version_canonical_upstream" type="TEXT" null="false"/> + <column name="version_canonical_release" type="TEXT" null="false" options="COLLATE "C""/> + <column name="version_revision" type="INTEGER" null="false"/> + <column name="version_upstream" type="TEXT" null="false"/> + <column name="version_release" type="TEXT" null="true"/> + <column name="priority" type="INTEGER" null="false"/> + <column name="priority_comment" type="TEXT" null="false"/> + <column name="summary" type="TEXT" null="false"/> + <column name="description" type="TEXT" null="true"/> + <column name="changes" type="TEXT" null="false"/> + <column name="url" type="TEXT" null="false"/> + <column name="url_comment" type="TEXT" null="false"/> + <column name="package_url" type="TEXT" null="true"/> + <column name="package_url_comment" type="TEXT" null="true"/> + <column name="email" type="TEXT" null="false"/> + <column name="email_comment" type="TEXT" null="false"/> + <column name="package_email" type="TEXT" null="true"/> + <column name="package_email_comment" type="TEXT" null="true"/> + <column name="build_email" type="TEXT" null="true"/> + <column name="build_email_comment" type="TEXT" null="true"/> + <column name="internal_repository" type="TEXT" null="true"/> + <column name="location" type="TEXT" null="true"/> + <column name="sha256sum" type="TEXT" null="true"/> + <column name="search_index" type="tsvector" null="true"/> + <primary-key> + <column name="name"/> + <column name="version_epoch"/> + <column name="version_canonical_upstream"/> + <column name="version_canonical_release"/> + <column name="version_revision"/> + </primary-key> + <foreign-key name="internal_repository_fk" deferrable="DEFERRED"> + <column name="internal_repository"/> + <references table="repository"> + <column name="name"/> + </references> + </foreign-key> + <index name="package_search_index_i" method="GIN"> + <column name="search_index"/> + </index> + </table> + <table name="package_license_alternatives" kind="container"> + <column name="name" type="TEXT" null="false"/> + <column name="version_epoch" type="INTEGER" null="false"/> + <column name="version_canonical_upstream" type="TEXT" null="false"/> + <column name="version_canonical_release" type="TEXT" null="false" options="COLLATE "C""/> + <column name="version_revision" type="INTEGER" null="false"/> + <column name="index" type="BIGINT" null="false"/> + <column name="comment" type="TEXT" null="false"/> + <foreign-key name="object_id_fk" on-delete="CASCADE"> + <column name="name"/> + <column name="version_epoch"/> + <column name="version_canonical_upstream"/> + <column name="version_canonical_release"/> + <column name="version_revision"/> + <references table="package"> + <column name="name"/> + <column name="version_epoch"/> + <column name="version_canonical_upstream"/> + <column name="version_canonical_release"/> + <column name="version_revision"/> + </references> + </foreign-key> + <index name="package_license_alternatives_object_id_i"> + <column name="name"/> + <column name="version_epoch"/> + <column name="version_canonical_upstream"/> + <column name="version_canonical_release"/> + <column name="version_revision"/> + </index> + <index name="package_license_alternatives_index_i"> + <column name="index"/> + </index> + </table> + <table name="package_licenses" kind="container"> + <column name="name" type="TEXT" null="false"/> + <column name="version_epoch" type="INTEGER" null="false"/> + <column name="version_canonical_upstream" type="TEXT" null="false"/> + <column name="version_canonical_release" type="TEXT" null="false" options="COLLATE "C""/> + <column name="version_revision" type="INTEGER" null="false"/> + <column name="alternative_index" type="BIGINT" null="false"/> + <column name="index" type="BIGINT" null="false"/> + <column name="license" type="TEXT" null="false"/> + <foreign-key name="object_id_fk" on-delete="CASCADE"> + <column name="name"/> + <column name="version_epoch"/> + <column name="version_canonical_upstream"/> + <column name="version_canonical_release"/> + <column name="version_revision"/> + <references table="package"> + <column name="name"/> + <column name="version_epoch"/> + <column name="version_canonical_upstream"/> + <column name="version_canonical_release"/> + <column name="version_revision"/> + </references> + </foreign-key> + <index name="package_licenses_object_id_i"> + <column name="name"/> + <column name="version_epoch"/> + <column name="version_canonical_upstream"/> + <column name="version_canonical_release"/> + <column name="version_revision"/> + </index> + </table> + <table name="package_tags" kind="container"> + <column name="name" type="TEXT" null="false"/> + <column name="version_epoch" type="INTEGER" null="false"/> + <column name="version_canonical_upstream" type="TEXT" null="false"/> + <column name="version_canonical_release" type="TEXT" null="false" options="COLLATE "C""/> + <column name="version_revision" type="INTEGER" null="false"/> + <column name="index" type="BIGINT" null="false"/> + <column name="tag" type="TEXT" null="false"/> + <foreign-key name="object_id_fk" on-delete="CASCADE"> + <column name="name"/> + <column name="version_epoch"/> + <column name="version_canonical_upstream"/> + <column name="version_canonical_release"/> + <column name="version_revision"/> + <references table="package"> + <column name="name"/> + <column name="version_epoch"/> + <column name="version_canonical_upstream"/> + <column name="version_canonical_release"/> + <column name="version_revision"/> + </references> + </foreign-key> + <index name="package_tags_object_id_i"> + <column name="name"/> + <column name="version_epoch"/> + <column name="version_canonical_upstream"/> + <column name="version_canonical_release"/> + <column name="version_revision"/> + </index> + <index name="package_tags_index_i"> + <column name="index"/> + </index> + </table> + <table name="package_dependencies" kind="container"> + <column name="name" type="TEXT" null="false"/> + <column name="version_epoch" type="INTEGER" null="false"/> + <column name="version_canonical_upstream" type="TEXT" null="false"/> + <column name="version_canonical_release" type="TEXT" null="false" options="COLLATE "C""/> + <column name="version_revision" type="INTEGER" null="false"/> + <column name="index" type="BIGINT" null="false"/> + <column name="conditional" type="BOOLEAN" null="false"/> + <column name="buildtime" type="BOOLEAN" null="false"/> + <column name="comment" type="TEXT" null="false"/> + <foreign-key name="object_id_fk" on-delete="CASCADE"> + <column name="name"/> + <column name="version_epoch"/> + <column name="version_canonical_upstream"/> + <column name="version_canonical_release"/> + <column name="version_revision"/> + <references table="package"> + <column name="name"/> + <column name="version_epoch"/> + <column name="version_canonical_upstream"/> + <column name="version_canonical_release"/> + <column name="version_revision"/> + </references> + </foreign-key> + <index name="package_dependencies_object_id_i"> + <column name="name"/> + <column name="version_epoch"/> + <column name="version_canonical_upstream"/> + <column name="version_canonical_release"/> + <column name="version_revision"/> + </index> + <index name="package_dependencies_index_i"> + <column name="index"/> + </index> + </table> + <table name="package_dependency_alternatives" kind="container"> + <column name="name" type="TEXT" null="false"/> + <column name="version_epoch" type="INTEGER" null="false"/> + <column name="version_canonical_upstream" type="TEXT" null="false"/> + <column name="version_canonical_release" type="TEXT" null="false" options="COLLATE "C""/> + <column name="version_revision" type="INTEGER" null="false"/> + <column name="dependency_index" type="BIGINT" null="false"/> + <column name="index" type="BIGINT" null="false"/> + <column name="dep_name" type="TEXT" null="false"/> + <column name="dep_version_epoch" type="INTEGER" null="false"/> + <column name="dep_version_canonical_upstream" type="TEXT" null="false"/> + <column name="dep_version_canonical_release" type="TEXT" null="false" options="COLLATE "C""/> + <column name="dep_version_revision" type="INTEGER" null="false"/> + <column name="dep_min_version_epoch" type="INTEGER" null="true"/> + <column name="dep_min_version_canonical_upstream" type="TEXT" null="true"/> + <column name="dep_min_version_canonical_release" type="TEXT" null="true"/> + <column name="dep_min_version_revision" type="INTEGER" null="true"/> + <column name="dep_min_version_upstream" type="TEXT" null="true"/> + <column name="dep_min_version_release" type="TEXT" null="true"/> + <column name="dep_max_version_epoch" type="INTEGER" null="true"/> + <column name="dep_max_version_canonical_upstream" type="TEXT" null="true"/> + <column name="dep_max_version_canonical_release" type="TEXT" null="true"/> + <column name="dep_max_version_revision" type="INTEGER" null="true"/> + <column name="dep_max_version_upstream" type="TEXT" null="true"/> + <column name="dep_max_version_release" type="TEXT" null="true"/> + <column name="dep_min_open" type="BOOLEAN" null="true"/> + <column name="dep_max_open" type="BOOLEAN" null="true"/> + <foreign-key name="object_id_fk" on-delete="CASCADE"> + <column name="name"/> + <column name="version_epoch"/> + <column name="version_canonical_upstream"/> + <column name="version_canonical_release"/> + <column name="version_revision"/> + <references table="package"> + <column name="name"/> + <column name="version_epoch"/> + <column name="version_canonical_upstream"/> + <column name="version_canonical_release"/> + <column name="version_revision"/> + </references> + </foreign-key> + <index name="package_dependency_alternatives_object_id_i"> + <column name="name"/> + <column name="version_epoch"/> + <column name="version_canonical_upstream"/> + <column name="version_canonical_release"/> + <column name="version_revision"/> + </index> + <foreign-key name="dep_package_fk" deferrable="DEFERRED"> + <column name="dep_name"/> + <column name="dep_version_epoch"/> + <column name="dep_version_canonical_upstream"/> + <column name="dep_version_canonical_release"/> + <column name="dep_version_revision"/> + <references table="package"> + <column name="name"/> + <column name="version_epoch"/> + <column name="version_canonical_upstream"/> + <column name="version_canonical_release"/> + <column name="version_revision"/> + </references> + </foreign-key> + </table> + <table name="package_requirements" kind="container"> + <column name="name" type="TEXT" null="false"/> + <column name="version_epoch" type="INTEGER" null="false"/> + <column name="version_canonical_upstream" type="TEXT" null="false"/> + <column name="version_canonical_release" type="TEXT" null="false" options="COLLATE "C""/> + <column name="version_revision" type="INTEGER" null="false"/> + <column name="index" type="BIGINT" null="false"/> + <column name="conditional" type="BOOLEAN" null="false"/> + <column name="buildtime" type="BOOLEAN" null="false"/> + <column name="comment" type="TEXT" null="false"/> + <foreign-key name="object_id_fk" on-delete="CASCADE"> + <column name="name"/> + <column name="version_epoch"/> + <column name="version_canonical_upstream"/> + <column name="version_canonical_release"/> + <column name="version_revision"/> + <references table="package"> + <column name="name"/> + <column name="version_epoch"/> + <column name="version_canonical_upstream"/> + <column name="version_canonical_release"/> + <column name="version_revision"/> + </references> + </foreign-key> + <index name="package_requirements_object_id_i"> + <column name="name"/> + <column name="version_epoch"/> + <column name="version_canonical_upstream"/> + <column name="version_canonical_release"/> + <column name="version_revision"/> + </index> + <index name="package_requirements_index_i"> + <column name="index"/> + </index> + </table> + <table name="package_requirement_alternatives" kind="container"> + <column name="name" type="TEXT" null="false"/> + <column name="version_epoch" type="INTEGER" null="false"/> + <column name="version_canonical_upstream" type="TEXT" null="false"/> + <column name="version_canonical_release" type="TEXT" null="false" options="COLLATE "C""/> + <column name="version_revision" type="INTEGER" null="false"/> + <column name="requirement_index" type="BIGINT" null="false"/> + <column name="index" type="BIGINT" null="false"/> + <column name="id" type="TEXT" null="false"/> + <foreign-key name="object_id_fk" on-delete="CASCADE"> + <column name="name"/> + <column name="version_epoch"/> + <column name="version_canonical_upstream"/> + <column name="version_canonical_release"/> + <column name="version_revision"/> + <references table="package"> + <column name="name"/> + <column name="version_epoch"/> + <column name="version_canonical_upstream"/> + <column name="version_canonical_release"/> + <column name="version_revision"/> + </references> + </foreign-key> + <index name="package_requirement_alternatives_object_id_i"> + <column name="name"/> + <column name="version_epoch"/> + <column name="version_canonical_upstream"/> + <column name="version_canonical_release"/> + <column name="version_revision"/> + </index> + </table> + <table name="package_other_repositories" kind="container"> + <column name="name" type="TEXT" null="false"/> + <column name="version_epoch" type="INTEGER" null="false"/> + <column name="version_canonical_upstream" type="TEXT" null="false"/> + <column name="version_canonical_release" type="TEXT" null="false" options="COLLATE "C""/> + <column name="version_revision" type="INTEGER" null="false"/> + <column name="index" type="BIGINT" null="false"/> + <column name="repository" type="TEXT" null="false"/> + <foreign-key name="object_id_fk" on-delete="CASCADE"> + <column name="name"/> + <column name="version_epoch"/> + <column name="version_canonical_upstream"/> + <column name="version_canonical_release"/> + <column name="version_revision"/> + <references table="package"> + <column name="name"/> + <column name="version_epoch"/> + <column name="version_canonical_upstream"/> + <column name="version_canonical_release"/> + <column name="version_revision"/> + </references> + </foreign-key> + <index name="package_other_repositories_object_id_i"> + <column name="name"/> + <column name="version_epoch"/> + <column name="version_canonical_upstream"/> + <column name="version_canonical_release"/> + <column name="version_revision"/> + </index> + <index name="package_other_repositories_index_i"> + <column name="index"/> + </index> + <foreign-key name="repository_fk" deferrable="DEFERRED"> + <column name="repository"/> + <references table="repository"> + <column name="name"/> + </references> + </foreign-key> + </table> + </model> +</changelog> diff --git a/libbrep/types.hxx b/libbrep/types.hxx new file mode 100644 index 0000000..a0001a7 --- /dev/null +++ b/libbrep/types.hxx @@ -0,0 +1,94 @@ +// file : libbrep/types.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef LIBBREP_TYPES_HXX +#define LIBBREP_TYPES_HXX + +#include <vector> +#include <string> +#include <memory> // unique_ptr, shared_ptr +#include <utility> // pair +#include <cstddef> // size_t, nullptr_t +#include <cstdint> // uint{8,16,32,64}_t +#include <istream> +#include <ostream> +#include <functional> // function, reference_wrapper + +#include <ios> // ios_base::failure +#include <exception> // exception +#include <stdexcept> // logic_error, invalid_argument, runtime_error +#include <system_error> + +#include <odb/lazy-ptr.hxx> + +#include <butl/path> +#include <butl/path-io> +#include <butl/optional> +#include <butl/timestamp> + +namespace brep +{ + // Commonly-used types. + // + using std::uint8_t; + using std::uint16_t; + using std::uint32_t; + using std::uint64_t; + + using std::size_t; + using std::nullptr_t; + + using std::pair; + using std::string; + using std::function; + using std::reference_wrapper; + + using std::unique_ptr; + using std::shared_ptr; + using std::weak_ptr; + + using std::vector; + + using strings = vector<string>; + using cstrings = vector<const char*>; + + using std::istream; + using std::ostream; + + // Exceptions. While <exception> is included, there is no using for + // std::exception -- use qualified. + // + using std::logic_error; + using std::invalid_argument; + using std::runtime_error; + using std::system_error; + using io_error = std::ios_base::failure; + + // <butl/optional> + // + using butl::optional; + using butl::nullopt; + + // ODB smart pointers. + // + using odb::lazy_shared_ptr; + using odb::lazy_weak_ptr; + + // <butl/path> + // + using butl::path; + using butl::dir_path; + using butl::basic_path; + using butl::invalid_path; + + using paths = std::vector<path>; + using dir_paths = std::vector<dir_path>; + + // <butl/timestamp> + // + using butl::timestamp; + using butl::timestamp_nonexistent; +} + +#endif // LIBBREP_TYPES_HXX diff --git a/libbrep/utility.hxx b/libbrep/utility.hxx new file mode 100644 index 0000000..f5948dd --- /dev/null +++ b/libbrep/utility.hxx @@ -0,0 +1,34 @@ +// file : libbrep/utility.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef LIBBREP_UTILITY_HXX +#define LIBBREP_UTILITY_HXX + +#include <memory> // make_shared() +#include <string> // to_string() +#include <utility> // move(), forward(), declval(), make_pair() +#include <cassert> // assert() +#include <iterator> // make_move_iterator() + +#include <butl/utility> // reverse_iterate(), operator<<(ostream, exception) + +namespace brep +{ + using std::move; + using std::forward; + using std::declval; + + using std::make_pair; + using std::make_shared; + using std::make_move_iterator; + using std::to_string; + + // <butl/utility> + // + using butl::reverse_iterate; +} + +#include <libbrep/version.hxx> + +#endif // LIBBREP_UTILITY_HXX diff --git a/libbrep/version.hxx.in b/libbrep/version.hxx.in new file mode 100644 index 0000000..e1b60a2 --- /dev/null +++ b/libbrep/version.hxx.in @@ -0,0 +1,74 @@ +// file : libbrep/version.hxx.in -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef BREP_VERSION // Note: using the version macro itself. + +// Note: using build2 standard versioning scheme. The numeric version format +// is AAABBBCCCDDDE where: +// +// AAA - major version number +// BBB - minor version number +// CCC - bugfix version number +// DDD - alpha / beta (DDD + 500) version number +// E - final (0) / snapshot (1) +// +// When DDDE is not 0, 1 is subtracted from AAABBBCCC. For example: +// +// Version AAABBBCCCDDDE +// +// 0.1.0 0000010000000 +// 0.1.2 0000010010000 +// 1.2.3 0010020030000 +// 2.2.0-a.1 0020019990010 +// 3.0.0-b.2 0029999995020 +// 2.2.0-a.1.z 0020019990011 +// +#define BREP_VERSION $brep.version.project_number$ULL +#define BREP_VERSION_STR "$brep.version.project$" +#define BREP_VERSION_ID "$brep.version.project_id$" + +#define BREP_VERSION_MAJOR $brep.version.major$ +#define BREP_VERSION_MINOR $brep.version.minor$ +#define BREP_VERSION_PATCH $brep.version.patch$ + +#define BREP_PRE_RELEASE $brep.version.pre_release$ + +#define BREP_SNAPSHOT $brep.version.snapshot_sn$ULL +#define BREP_SNAPSHOT_ID "$brep.version.snapshot_id$" + +#include <butl/version> + +$libbutl.check(LIBBUTL_VERSION, LIBBUTL_SNAPSHOT)$ + +#include <bpkg/version> + +$libbpkg.check(LIBBPKG_VERSION, LIBBPKG_SNAPSHOT)$ + +#include <libbbot/version.hxx> + +$libbbot.check(LIBBBOT_VERSION, LIBBBOT_SNAPSHOT)$ + +#include <odb/version.hxx> + +$libodb.check(LIBODB_VERSION, LIBODB_SNAPSHOT)$ + +#include <odb/pgsql/version.hxx> + +$libodb-pgsql.check(LIBODB_PGSQL_VERSION, LIBODB_PGSQL_SNAPSHOT)$ + +// @@ Not really the correct place for the check since we don't use +// it here. +// +/* +#include <xml/version> + +$libstudxml.check(LIBSTUDXML_VERSION, LIBSTUDXML_SNAPSHOT)$ +*/ + +// For now these are the same. +// +#define LIBBREP_VERSION BREP_VERSION +#define LIBBREP_VERSION_STR BREP_VERSION_STR + +#endif // BREP_VERSION diff --git a/libbrep/wrapper-traits.hxx b/libbrep/wrapper-traits.hxx new file mode 100644 index 0000000..ac039e3 --- /dev/null +++ b/libbrep/wrapper-traits.hxx @@ -0,0 +1,63 @@ +// file : libbrep/wrapper-traits.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef LIBBREP_WRAPPER_TRAITS_HXX +#define LIBBREP_WRAPPER_TRAITS_HXX + +#include <odb/pre.hxx> + +#include <butl/optional> + +#include <odb/wrapper-traits.hxx> + +namespace odb +{ + template <typename T> + class wrapper_traits<butl::optional<T>> + { + public: + typedef T wrapped_type; + typedef butl::optional<T> wrapper_type; + + // T can be const. + // + typedef + typename odb::details::meta::remove_const<T>::result + unrestricted_wrapped_type; + + static const bool null_handler = true; + static const bool null_default = true; + + static bool + get_null (const wrapper_type& o) + { + return !o; + } + + static void + set_null (wrapper_type& o) + { + o = wrapper_type (); + } + + static const wrapped_type& + get_ref (const wrapper_type& o) + { + return *o; + } + + static unrestricted_wrapped_type& + set_ref (wrapper_type& o) + { + if (!o) + o = unrestricted_wrapped_type (); + + return const_cast<unrestricted_wrapped_type&> (*o); + } + }; +} + +#include <odb/post.hxx> + +#endif // LIBBREP_WRAPPER_TRAITS_HXX |